zoukankan      html  css  js  c++  java
  • KMP算法

    假设主串:S: S[1] S[2] S[3] ……S[n]

    模式串:T: T[1] T[2] T[3]…..T[m]

    现在我们假设主串第i 个字符与模式串的第j(j<=m)个字符‘失配’后,主串第i 个字符与模式串的第k(k<j)个字符继续比较,此时就有S[i] != T[j]

    主串:   S[1]...S[i-j+1]...S[i-1]S[i]...

                        ||(匹配)   ||   

    模式串:            T[1]...  T[j-1] T[j]

    由此,可以得到关系式如下

      T[1]T[2]T[3]...T[j-1] = S[i-j+1]...S[i-1]

    由于S[i] != T[j],接下来S[i]将与T[k]继续比较,则模式串中的前k-1咯字符串必须满足下列关系式,并且不可能存在k'>k满足下列关系式:

    T[1]T[2]T[3]...T[k-1] = S[j-k+1]S[j-k+2]...S[i-1] (k<j)

    也就是说:

    主串:  S[1]...S[i-k+1]S[i-k+2]...S[i-1]S[i]...

                        ||        ||         ||    ?(待比较)

    模式串:           T[1]      T[2]...  T[k-1] T[k]

    现在可以把前面的关系综合总结如下

    S[1]...S[i-j+1]...S[i-k+1]S[i-k+2]...S[i-1]S[i]...

                ||          ||       ||           ||  

               T[1]...    T[j-k+1] T[j-k+2]...   T[j-1] T[j]

                             ||       ||            ||    ?

                            T[1]     T[2] ...     T[k-1] T[k]

    现在唯一的任务就是如何求k了,通过一个next函数求。

    /*
    pku3461(Oulipo), hdu1711(Number Sequence)
    这个模板 字符串是从0开始的
    Next数组是从1开始的
    
    
    */
    #include <iostream>
    #include <cstring>
    using namespace std;
    
    const int N = 1000002;
    int next[N];
    char S[N], T[N];
    int slen, tlen;
    
    void getNext()
    {
        int j, k;
        j = 0; k = -1; next[0] = -1;
        while(j < tlen)
            if(k == -1 || T[j] == T[k])
                next[++j] = ++k;
            else
                k = next[k];
    
    }
    /*
    返回模式串T在主串S中首次出现的位置
    返回的位置是从0开始的。
    */
    int KMP_Index()
    {
        int i = 0, j = 0;
        getNext();
    
        while(i < slen && j < tlen)
        {
            if(j == -1 || S[i] == T[j])
            {
                i++; j++;
            }
            else
                j = next[j];
        }
        if(j == tlen)
            return i - tlen;
        else
            return -1;
    }
    /*
    返回模式串在主串S中出现的次数
    */
    int KMP_Count()
    {
        int ans = 0;
        int i, j = 0;
    
        if(slen == 1 && tlen == 1)
        {
            if(S[0] == T[0])
                return 1;
            else
                return 0;
        }
        getNext();
        for(i = 0; i < slen; i++)
        {
            while(j > 0 && S[i] != T[j])
                j = next[j];
            if(S[i] == T[j])
                j++;
            if(j == tlen)
            {
                ans++;
                j = next[j];
            }
        }
        return ans;
    }
    int main()
    {
        
        int TT;
        int i, cc;
        cin>>TT;
        while(TT--)
        {
            cin>>S>>T;
            slen = strlen(S);
            tlen = strlen(T);
            cout<<"模式串T在主串S中首次出现的位置是: "<<KMP_Index()<<endl;
            cout<<"模式串T在主串S中出现的次数为: "<<KMP_Count()<<endl;
        }
        return 0;
    }
    /*
    test case
    4
    aaaaaa a
    abcd d
    aabaa b
    */

    下面是几个例子

    HDU 1711

    找子串首次出现的位置:

    #include<stdio.h>
    int a[1000010],b[10010];
    int next[10010];
    int n,m;
    void getNext()
    {
        int j,k;
        j=0;
        k=-1;
        next[0]=-1;
        while(j<m)
        {
            if(k==-1||b[j]==b[k])
              next[++j]=++k;
            else k=next[k];
        }    
    }  
    //返回首次出现的位置 
    int KMP_Index()
    {
        int i=0,j=0;
        getNext();
        
        while(i<n && j<m)
        {
            if(j==-1||a[i]==b[j])
            {
                i++;
                j++;
            }    
            else j=next[j];
            
        }    
        if(j==m) return i-m+1;
        else return -1;
    }      
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            for(int i=0;i<n;i++)
              scanf("%d",&a[i]);
            for(int i=0;i<m;i++)
              scanf("%d",&b[i]);
            printf("%d\n",KMP_Index());
        }    
        return 0;
    }    
    /*
    POJ   3461 Oulipo
    统计子串出现的次数
    */
    #include<stdio.h>
    #include<string.h>
    #include<iostream>
    using namespace std;
    char W[10010],T[1000010];
    int wlen,tlen;
    int next[10010];
    void getNext()
    {
        int j,k;
        j=0;
        k=-1;
        next[0]=-1;
        while(j<wlen)
        {
            if(k==-1||W[j]==W[k])
            {
                next[++j]=++k;
            }
            else k=next[k];
        }
    }
    int KMP_count()
    {
        int ans=0;
        int i,j=0;
        if(wlen==1&&tlen==1)
        {
            if(W[0]==T[0])return 1;
            else return 0;
        }
        getNext();
        for(i=0;i<tlen;i++)
        {
            while(j>0&&T[i]!=W[j])
              j=next[j];
            if(W[j]==T[i])j++;
            if(j==wlen)
            {
                ans++;
                j=next[j];
            }
        }
        return ans;
    }
    int main()
    {
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
        int tcase;
        scanf("%d",&tcase);
        while(tcase--)
        {
            scanf("%s%s",&W,&T);
            wlen=strlen(W);
            tlen=strlen(T);
    
            printf("%d\n",KMP_count());
        }
        return 0;
    }
    /*
    POJ 1611 Period
    Sample Input
    3
    aaa
    12
    aabaabaabaab
    0
    Sample Output
    Test case #1
    2 2
    3 3
    
    Test case #2
    2 2
    6 2
    9 3
    12 4
    
    
    题意就是求一个字符串中的重复子串
    */
    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    
    const int MAXN=1000010;
    char str[MAXN];
    int next[MAXN];
    int n;
    void getNext()
    {
        int j,k;
        j=0;
        k=-1;
        next[0]=-1;
        while(str[j]!='\0')
        {
            if(k==-1||str[j]==str[k])
            {
                j++;
                k++;
                if(j%(j-k)==0&&j/(j-k)>1)
                  printf("%d %d\n",j,j/(j-k));
                next[j]=k;
            }
            else k=next[k];
        }
    }
    int main()
    {
       // freopen("in.txt","r",stdin);
       // freopen("out.txt","w",stdout);
        int iCase=0;
        while(scanf("%d",&n),n)
        {
            iCase++;
            scanf("%s",&str);
            printf("Test case #%d\n",iCase);
            getNext();
            printf("\n");
        }
        return 0;
    }


     

    人一我百!人十我万!永不放弃~~~怀着自信的心,去追逐梦想
  • 相关阅读:
    【ES】学习10-聚合3
    Flink – CEP NFA
    cruise-control
    FlinkCEP
    Flink
    SQL Join
    Flink – Stream Task执行过程
    Flink
    Java8
    Flink
  • 原文地址:https://www.cnblogs.com/kuangbin/p/2638803.html
Copyright © 2011-2022 走看看