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

...

原文地址:https://www.cnblogs.com/myrtle/p/11329515.html