zoukankan      html  css  js  c++  java
  • kmp算法模板

    目的:在字符串T中查找字符串P的出现位置,预处理P字符串得到fail数组

    时间复杂度:O(|P|+|T|)

    next[i]定义为字符s[i]前面最多有多少个连续的字符和字符串s从初始位置开始的字符匹配,i∈[1,n]

    循环节问题:

    字符串长度为n,若n%(n-Next[n])==0,必有循环节,且循环节长为n-Next[n];

    n%(n-Next[n])!=0时可能存在错位、递推周期性相等的情况,造成不完全循环,此时循环周期依然为n-Next[n]。

    自用模板:

    #include <iostream>
    #include <string.h>
    using namespace std;
    int Next[1110];
    void get_Next(char *p)
    {
        int m=strlen(p);
        Next[0]=Next[1]=0;
        for(int i=1;i<m;i++)
        {
            int j=Next[i];
            while(j&&p[i]!=p[j])
            {j=Next[j];}
            Next[i+1]=p[i]==p[j]?j+1:0;
        }
    }
    int kmp(char *T,char *s)
    {
        int n=strlen(T),m=strlen(s);
        get_Next(s);
        int j=0;
        for(int i=0;i<n;i++)
        {
            while(j&&T[i]!=s[j])
            {j=Next[j];}
            if(s[j]==T[i])
            {j++;}
            if(j==m)
            {
                return i-m+1;
            }
        }
        return -1;
    }
    int main()
    {
        char s[1100],T[1100];
        cin>>T>>s;
        cout<<kmp(T,s)<<endl;  //在T中找s
        return 0;
    }
    View Code

    修改模板(在字符串T中查找P的出现次数):

    int kmp(char *T,char *s)
    {
        int n=strlen(T),m=strlen(s),ans=0;
        get_Next(s);
        int j=0;
        for(int i=0;i<n;i++)
        {
            while(j&&T[i]!=s[j])
            {j=Next[j];}
            if(s[j]==T[i])
            {j++;}
            if(j==m)
            {
                ans++;
                j=Next[j];
             } 
        }
        return ans;
    }
    View Code

    循环节长度len-Next[len]

    hdu3746,末尾加珠子,变成有循环节的手链,至少加几颗

    https://blog.csdn.net/a1097304791/article/details/100538195

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+10;
    int Next[maxn];
    void get_Next(char *p)
    {
        int m=strlen(p);
        Next[0]=Next[1]=0;
        for(int i=1;i<m;i++)
        {
            int j=Next[i];
            while(j&&p[i]!=p[j])
            {j=Next[j];}
            Next[i+1]=p[i]==p[j]?j+1:0;
        }
    }
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            char s[maxn];
            scanf("%s",s);
            get_Next(s);
            int len=strlen(s),m=0;
            for(int i=1;i<=len;i++)
            {
                m=max(m,i-Next[i]);
            }
            if(Next[len]==0)printf("%d
    ",m);
            else if(len%m==0)printf("%d
    ",0);
            else printf("%d
    ",m-len%m);
        }
        return 0;
    }
    View Code

    hdu1358,求字符串的前缀是否为周期串,打印循环节的长度及循环次数

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+10;
    int Next[maxn];
    void get_next(char *p)
    {
        int m=strlen(p);
        Next[0]=Next[1]=0;
        for(int i=1;i<m;i++)
        {
            int j=Next[i];
            while(j&&p[i]!=p[j])j=Next[j];
            Next[i+1]=p[i]==p[j]?j+1:0;
        }
    }
    int main()
    {
        int n,t=1;
        while(scanf("%d",&n))
        {
            if(n==0)return 0;
            printf("Test case #%d
    ",t++);
            char s[maxn];
            scanf("%s",s);
            get_next(s);
            int len=strlen(s),m=0;
            for(int i=1;i<=len;i++)
            {
    
                if(i%(i-Next[i])==0&&i/(i-Next[i])!=1)
                printf("%d %d
    ",i,i/(i-Next[i]));
            }
            printf("
    ");
        }
        return 0;
    }
    View Code

    poj2406,给出一个字符串,问它最多由多少相同的字串组成 ,如abababab由4个ab组成

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    using namespace std;
    const int maxn=1e6+10;
    int Next[maxn];
    void get_next(char *p)
    {
        int m=strlen(p);
        Next[0]=Next[1]=0;
        for(int i=1;i<m;i++)
        {
            int j=Next[i];
            while(j&&p[i]!=p[j])j=Next[j];
            Next[i+1]=p[i]==p[j]?j+1:0;
        }
    }
    
    int main()
    {
        char ch[maxn];
        while(scanf("%s",ch))
        {
            if(ch[0]=='.'&&strlen(ch)==1)return 0;
            get_next(ch);
            int len=strlen(ch),ans,m=len-Next[len];
            
            if(Next[len]==0||len%m!=0)ans=1;
            else ans=len/m;
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    poj2752,给你一串字符串s找到所有的公共前后缀,即既是前缀又是后缀的子串

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<stack>
    using namespace std;
    const int maxn=4e5+10;
    int Next[maxn];
    void get_next(char *p)
    {
        int m=strlen(p);
        Next[0]=Next[1]=0;
        for(int i=1;i<m;i++)
        {
            int j=Next[i];
            while(j&&p[i]!=p[j])j=Next[j];
            Next[i+1]=p[i]==p[j]?j+1:0;
        }
    }
    
    int main()
    {
        char ch[maxn];
        while(scanf("%s",ch)!=EOF)
        {
            int len=strlen(ch);
            get_next(ch);
            stack<int> s;
            int tmp=len;
            while(tmp!=0)
            {
                s.push(tmp);
                tmp=Next[tmp];
            }
            while(!s.empty())
            {
                if(s.size()==1)printf("%d
    ",s.top());
                else printf("%d ",s.top());
                s.pop();
            }
        }
        return 0;
    }
    View Code

    hdu2954,kmp&ex-kmp都可做,求s1的前缀和s2的后缀的最长公共长度

    kmp做法

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+10;
    int Next[maxn];
    void get_next(char *p)
    {
        int m=strlen(p);
        Next[0]=Next[1]=0;
        for(int i=1;i<m;i++)
        {
            int j=Next[i];
            while(j&&p[i]!=p[j])j=Next[j];
            Next[i+1]=p[i]==p[j]?j+1:0;
        }
    }
    
    
    int main()
    {
        char ch[maxn],ch2[maxn];
        while(scanf("%s%s",ch,ch2)!=EOF)
        {
            int len1=strlen(ch),len2=strlen(ch2);
            
            strcat(ch,ch2);
            get_next(ch);
            int res=Next[strlen(ch)];
            
            while(res>len1||res>len2)res=Next[res];
            if(res==0)printf("%d
    ",res);
            else 
            {
                ch[res]='';
                printf("%s ",ch);
                printf("%d
    ",res);
            } 
        }
        return 0;
    }
    View Code

    exkmp做法

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+10;
    int Next[maxn],extend[maxn];
    void get_next(char *s)
    {
        int n=strlen(s),i,j,k;
        for(j=0;1+j<n&&s[j]==s[1+j];j++);
        Next[1]=j;
        k=1;
        for(i=2;i<n;i++)
        {
            int len=k+Next[k],L=Next[i-k];
            if(L<len-i)Next[i]=L;
            else 
            {
                for(j=max(0,len-i);i+j<n&&s[j]==s[i+j];j++);
                Next[i]=j;
                k=i;
            }
        }
        Next[0]=n;
    }
    void ex_kmp(char *t,char *s)
    {
        int n=strlen(t),m=strlen(s),i,j,k;
        for(j=0;j<n&&j<m&&t[j]==s[j];j++);
        extend[0]=j;
        k=0;
        for(i=1;i<n;i++)
        {
            int len=k+extend[k],L=Next[i-k];
            if(L<len-i)extend[i]=L;
            else
            {
                for(j=max(0,len-i);j<m&&i+j<n&&s[j]==t[i+j];j++);
                extend[i]=j;
                k=i;
            }
        }
    }
    
    int main()
    {
        char s[maxn],t[maxn];
        while(scanf("%s%s",s,t)!=EOF)
        {
            get_next(s);
            ex_kmp(t,s);
            int res=0,lent=strlen(t),lens=strlen(s);
            for(int i=0;i<lent;i++)
            {
                if(extend[i]+i==lent)
                res=max(res,extend[i]);
            }
            if(res==0)printf("0
    ");
            else
            {
                s[res]='';
                printf("%s %d
    ",s,res);
            }
        }
        return 0;
    }
    View Code

    hdu3366,求所有前缀出现的次数之和

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+10;
    int Next[maxn];
    void get_next(char *p)
    {
        int m=strlen(p);
        Next[0]=Next[1]=0;
        for(int i=1;i<m;i++)
        {
            int j=Next[i];
            while(j&&p[i]!=p[j])j=Next[j];
            Next[i+1]=p[i]==p[j]?j+1:0;
        }
    }
    int main()
    {
        int T,mod=10007;
        scanf("%d",&T);
        while(T--)
        {
            int len,res[maxn]={0},ans=0;
            char ch[maxn];
            scanf("%d",&len);
            scanf("%s",ch);
            get_next(ch);
            for(int i=1;i<=len;i++)res[i]=1;
            for(int i=len;i>0;i--)
            {
                res[Next[i]]=(res[Next[i]]+res[i])%mod;
                ans=(ans+res[i]%mod)%mod;
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    hdu4763,求同时出现在串开头、末尾、中间的最长长度,如aabaabaa就是2

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+10;
    int Next[maxn];
    void get_next(char *p)
    {
        int m=strlen(p);
        Next[0]=Next[1]=0;
        for(int i=1;i<m;i++)
        {
            int j=Next[i];
            while(j&&p[i]!=p[j])j=Next[j];
            Next[i+1]=p[i]==p[j]?j+1:0;
        }
    }
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            char s[maxn];
            scanf("%s",s);
            get_next(s);
            map<int,int>mp;
            int len=strlen(s),ans=0,tmp=len;
            for(int i=2;i<len;i++)
            {
                mp[Next[i]]++;
            }
            while(Next[tmp])
            {
                if(mp[Next[tmp]]!=0){ans=Next[tmp];break;}
                tmp=Next[tmp];
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    n个串最长公共子串系列:

    hdu1238,找出最长的一个子串,所有串中都出现过该串or反着的该串,暴力可做,n=1可特判,输出串长度

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=300;
    int Next[maxn],n;
    void get_next(char *p)
    {
        int m=strlen(p);
        Next[0]=Next[1]=0;
        for(int i=1;i<m;i++)
        {
            int j=Next[i];
            while(j&&p[i]!=p[j])j=Next[j];
            Next[i+1]=p[i]==p[j]?j+1:0;
        }
    }
    int kmp(char *t,char *s)
    {
        int n=strlen(t),m=strlen(s);
        get_next(s);
        int j=0,maxn=0;
        for(int i=0;i<n;i++)
        {
            while(j&&t[i]!=s[j])j=Next[j],maxn=max(maxn,j);
            if(s[j]==t[i])j++;
            maxn=max(j,maxn);
            if(j==m||j==n)return j;
        }
        if(maxn>m||maxn>n)maxn=min(n,m);
        return maxn;
    }
    
    char ch[4050][250];
    char ch2[250];
    int check(int tmp)
    {
        int res=strlen(ch[0]);
        for(int i=1;i<n;i++)
        {
            int len=strlen(ch[i]),k=0;
            for(int j=len-1;j>=0;j--)
                ch2[k++]=ch[i][j];
                ch2[k]='';
            res=min(res,max(kmp(ch[i],ch[0]+tmp),kmp(ch2,ch[0]+tmp)));
        }
        return res;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for(int i=0;i<n;i++)scanf("%s",ch[i]);
            int res=0;
            for(int i=0;i<strlen(ch[0]);i++)
            {
                res=max(res,check(i));
            }
            printf("%d
    ",res);
        }
        return 0;
    }
    View Code

    hdu2328,字典序最小的最长公共子串,输出该串

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=300;
    int Next[maxn],n;
    void get_next(char *p)
    {
        int m=strlen(p);
        Next[0]=Next[1]=0;
        for(int i=1;i<m;i++)
        {
            int j=Next[i];
            while(j&&p[i]!=p[j])j=Next[j];
            Next[i+1]=p[i]==p[j]?j+1:0;
        }
    }
    int kmp(char *t,char *s)
    {
        int n=strlen(t),m=strlen(s);
        get_next(s);
        int j=0,maxn=0;
        for(int i=0;i<n;i++)
        {
            while(j&&t[i]!=s[j])j=Next[j],maxn=max(maxn,j);
            if(s[j]==t[i])j++;
            maxn=max(j,maxn);
            if(j==m||j==n)return j;
        }
        if(maxn>m||maxn>n)maxn=min(n,m);
        return maxn;
    }
    
    char ch[4050][maxn];
    int check(int tmp,int q)
    {
        int res=strlen(ch[q]);
        for(int i=1;i<n;i++)
        {
            res=min(res,kmp(ch[i],ch[q]+tmp));
        }
        return res;
    }
    int main()
    {
        while(scanf("%d",&n)!=EOF)
        {
            if(n==0)return 0;
            int lenmin=0;
            for(int i=0;i<n;i++)
            {
                scanf("%s",ch[i]);
                if(strlen(ch[i])<strlen(ch[lenmin]))
                    lenmin=i;
            }
            char res[maxn]={''},res2[maxn]={''};
            int ans=0;
            for(int i=0;i<strlen(ch[lenmin]);i++)  //我怎么又写成了n....
            {
                int tmp=check(i,lenmin);
                if(tmp>ans)
                {
                    ans=tmp;
                    strncpy(res,ch[lenmin]+i,tmp);res[tmp]='';
                }
                else if(tmp==ans)
                {
                    strncpy(res2,ch[lenmin]+i,tmp);res2[tmp]='';
                    if(strcmp(res,res2)>0)
                    strcpy(res,res2);
                }
            }
            if(strlen(res)>0)printf("%s
    ",res);
            else printf("IDENTITY LOST
    ");
        }
        return 0;
    }
    View Code

    hdu2328,后缀数组+二分做法

    https://www.cnblogs.com/geloutingyu/p/7467440.html

    ...

  • 相关阅读:
    [算法专题] 深度优先搜索&回溯剪枝
    [算法专题] 二分搜索&排序数组
    rand_1tom 产生 rand_1ton
    [LeetCode] Kth Largest Element in an Array
    进程控制(Note for apue and csapp)
    Transport Layer Protocols
    Internetworking
    Dynamic Programming | Set 4 (Longest Common Subsequence)
    Dynamic Programming | Set 3 (Longest Increasing Subsequence)
    Dynamic Programming | Set 2 (Optimal Substructure Property)
  • 原文地址:https://www.cnblogs.com/myrtle/p/11329515.html
Copyright © 2011-2022 走看看