KMP模板以及简单的入门题总结

KMP模板

//kmp算法的主要作用在于对next数组的运用,所以这里只给出next数组的模板
//性质1:对于每一个长度len的子串,该子串的最小循环节为len-next[len]
//性质2:kmp的next不断向前递归的过程可以保证对于每一个当前前缀,都有一段后缀与之对应
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std ;
const int maxn = 1e6+5;
int Next[maxn];
char mo[maxn];
int n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int main()
{
    scanf("%s",mo);
    n2=strlen(mo);
    Next[0]=-1;
    GetNext();
    return 0;
}

KMP第一题

HDU1711
题意就是给你两个序列,让你求B序列在A序列第一次出现(完全相同)的下标
本题就是KMP的模板题,将i指针指向A串,将j指针指向B串,如果匹配就继续下一位的匹配,如果不匹配,将j跳转到next[j],继续向前匹配。
HDU1711代码

#include<stdio.h>
#include<iostream>
using namespace std ;
const int maxn = 1e6+5;
int Next[maxn];
int str[maxn];
int mo[maxn];
int n1,n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int kmp()
{
    int cnt=0;
    int i=0,j=0;
    while(i<n1)
    {
        if(j==-1||str[i]==mo[j]) i++,j++;
        else j=Next[j];//next数组寻找与当前后缀匹配最长的前缀,省略不必要的查找
        if(j==n2)
            return i-n2+1;//首地址
    }
    return -1;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n1,&n2);
        for(int i=0;i<n1;i++) scanf("%d",&str[i]);
        for(int j=0;j<n2;j++) scanf("%d",&mo[j]);
        Next[0]=-1;
        GetNext();
        printf("%d\n",kmp());
    }
    return 0;
}

KMP第二题

HDU1686
题意就是求B串在A串中的出现次数(可重叠
依旧是利用next数组,当某次匹配完成之后,将ans++,然后把j跳转到next[j],原理类似上一题的不匹配的情况,省略不必要的查找。
HDU1686代码

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std ;
const int maxn = 1e6+5;
int Next[maxn];
char str[maxn];
char mo[maxn];
int n1,n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int kmp()
{
    int cnt=0;
    int i=0,j=0;
    while(i<n1)
    {
        if(j==-1||str[i]==mo[j]) i++,j++;
        else j=Next[j];
        if(j==n2)
        {
            cnt++;
            j=Next[j];//完成一次匹配,将j移动到最长的前缀处,省略不必要的查找
        }
    }
    return cnt;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s%s",mo,str);
        n1=strlen(str);
        n2=strlen(mo);
        Next[0]=-1;
        GetNext();
        printf("%d\n",kmp());
    }
    return 0;
}

KMP第三题

HDU2087
题意就是求B串在A串中的出现次数(不可重叠)
做法和上题类似,只不过在每次完成匹配之后,要考虑不能重叠的问题,所以j不能跳转到next[j],而应该从0重新开始,因为A中已经匹配过的字符不能再利用,所以就变成了A剩下的子串中找B出现的次数,所以j应该从0重新开始匹配。
HDU2087代码

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std ;
const int maxn = 1e6+5;
int Next[maxn];
char str[maxn];
char mo[maxn];
int n1,n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int kmp()
{
    int cnt=0;
    int i=0,j=0;
    while(i<n1)
    {
        if(j==-1||str[i]==mo[j]) i++,j++;
        else j=Next[j];
        if(j==n2)
        {
            cnt++;
            j=0;
        }
    }
    return cnt;
}
int main()
{
    while(scanf("%s",str)!=EOF)
    {
        n1=strlen(str);
        if(n1==1&&str[0]=='#') break;
        scanf("%s",mo);
        n2=strlen(mo);
        Next[0]=-1;
        GetNext();
        printf("%d\n",kmp());
    }
    return 0;
}

KMP第四题

HDU3746
本题题意为添加最少的字符使原字符串变成周期至少为2的循环字符串
用到模板里所说的,长度为len的字符串的最小循环节为len-next[len],求出最小循环节,算出最后应该补充多少就结束了。
求最小循环节的证明法请看此链接KMP求字符串最小循环节
HDU3746代码

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std ;
const int maxn = 1e6+5;
int Next[maxn];
char str[maxn];
char mo[maxn];
int n1,n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",mo);
        n2=strlen(mo);
        Next[0]=-1;
        GetNext();
        int tmp=n2-Next[n2];
        if(n2%tmp==0&&n2!=tmp)
        {
            printf("0\n");
        }
        else
        {
            printf("%d\n",tmp-n2%tmp);
        }
    }
    return 0;
}

KMP第五题

HDU1358
本题题意为求给定字符串中所有为循环串的前缀,并输出该前缀的最后一个字符下标和周期
用到模板中给定的方法,只要对每个字符串判定一下是否为循环串就可以了
HDU1358代码

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std ;
const int maxn = 1e6+5;
int Next[maxn];
char str[maxn];
char mo[maxn];
int n1,n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int main()
{
    int cnt=1;
    while(scanf("%d",&n2)!=EOF)
    {
        if(n2==0) break;
        scanf("%s",mo);
        Next[0]=-1;
        GetNext();
        printf("Test case #%d\n",cnt++);
        for(int i=1;i<=n2;i++)
        {
            int tmp=i-Next[i];
            if(i/tmp==1) continue;
            if(i%tmp==0)
            {
                printf("%d %d\n",i,i/tmp);
            }
        }
        printf("\n");
    }
    return 0;
}

KMP第六题

POJ2406
本题题意为求给定字符串的最大周期,做法类似上题,直接上代码
POJ2406代码

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std ;
const int maxn = 1e6+5;
int Next[maxn];
char str[maxn];
char mo[maxn];
int n1,n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int main()
{
    int cnt=1;
    while(scanf("%s",mo)!=EOF)
    {
        n2=strlen(mo);
        if(n2==1&&mo[0]=='.') break;
        Next[0]=-1;
        GetNext();
        int tmp=n2-Next[n2];
        if(n2%tmp==0) printf("%d\n",n2/tmp);
        else printf("1\n");
    }
    return 0;
}

KMP第七题

POJ2752
本题题意为求出所有在后缀中出现过的前缀的最后一个元素的下标
本题要考虑一下next数组的本质,其实就是最长的出现在后缀中的前缀,但是由于本题要求所有的而不是最长的,考虑到next数组的递归过程,其实就是对每一个当前长度的前缀,都有完全相同的后缀与之对应,所以就不断递归next数组即可求解。
POJ2752代码

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stack>
using namespace std ;
const int maxn = 1e6+5;
int ans[maxn];
int Next[maxn];
char str[maxn];
char mo[maxn];
int n1,n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int main()
{
    int cnt;
    while(scanf("%s",mo)!=EOF)
    {
        cnt=0;
        n2=strlen(mo);
        Next[0]=-1;
        GetNext();
        int j=n2;
        while(j!=0)
        {
            ans[cnt++]=j;
            j=Next[j];
        }
        for(int i=cnt-1;i>=0;i--)
        {
            printf("%d%c",ans[i],i==0?'\n':' ');
        }
    }
    return 0;
}

KMP第八题

POJ3080
本题题意为求m个字符串长度至少为3的最长公共子串
由于m只有10而且len小于60,我们可以选择枚举某一个串的子串并用str.find()或者kmp验证是否所有该子串在所有字符串中出现过,也可以用经典的二分长度将height数组分块的后缀数组做法
POJ3080(find解法

//由于只查找是否出现过,算法复杂度差距不大,所以这里给出简单一些的写法
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
using namespace std;
const int maxn = 65;
char str[maxn];
string ansstr;
string str2[maxn];
int main()
{
    int n,t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%s",str);
            str2[i]=str;
        }
        int ans=0;
        string tmp=str2[0];
        int tmplen=tmp.size();
        for(int i=0;i<tmplen;i++)
        {
            for(int j=1;i+j<=tmplen;j++)
            {
                int cnt=1;
                string ss=tmp.substr(i,j);
                for(int k=1;k<n;k++)
                {
                    if(str2[k].find(ss)!=-1)
                        cnt++;
                }
                if(cnt==n)
                {
                    if(ans<j)
                    {
                        ans=j;
                        ansstr=ss;
                    }
                    else if(ans==j)
                    {
                        ansstr=min(ansstr,ss);
                    }
                }
            }
        }
        if(ans<3) printf("no significant commonalities\n");
        else printf("%s\n",ansstr.c_str());
    }
}

POJ3080(后缀数组写法

//具体实现原理可以参考我的后缀数组博客
#include <iostream>
#include<algorithm>
#include <stdio.h>
#include <string.h>
using namespace std;
#define maxn 4005
const int INF = 0x3f3f3f3f;
int wa[maxn],wb[maxn],wsf[maxn],wv[maxn],sa[maxn];
int rank[maxn],height[maxn],s[maxn];
char str[15][65];
int t,lenn[maxn];
int belong[maxn];
int anspos;
int vis[65];
int cmp(int *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
void getsa(int *r,int *sa,int n,int m)
{
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0; i<m; i++)  wsf[i]=0;
    for(i=0; i<=n; i++)  wsf[x[i]=r[i]]++;
    for(i=1; i<m; i++)  wsf[i]+=wsf[i-1];
    for(i=n; i>=0; i--)  sa[--wsf[x[i]]]=i;
    p=1;
    j=1;
    for(; p<=n; j*=2,m=p)
    {
        for(p=0,i=n+1-j; i<=n; i++)  y[p++]=i;
        for(i=0; i<=n; i++)  if(sa[i]>=j)  y[p++]=sa[i]-j;
        for(i=0; i<=n; i++)  wv[i]=x[y[i]];
        for(i=0; i<m; i++)  wsf[i]=0;
        for(i=0; i<=n; i++)  wsf[wv[i]]++;
        for(i=1; i<m; i++)  wsf[i]+=wsf[i-1];
        for(i=n; i>=0; i--)  sa[--wsf[wv[i]]]=y[i];
        t=x;
        x=y;
        y=t;
        x[sa[0]]=0;
        for(p=1,i=1; i<=n; i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)? p-1:p++;
    }
}
void getheight(int *r,int n)
{
    int i,j,k=0;
    for(i=1; i<=n; i++)  rank[sa[i]]=i;
    for(i=0; i<n; i++)
    {
        if(k)
            k--;
        else
            k=0;
        j=sa[rank[i]-1];
        while(r[i+k]==r[j+k])
            k++;
        height[rank[i]]=k;
    }
}
int check(int x,int n)
{
    for(int i=1;i<=n-1;i++)
    {
        if(height[i]<x) continue;
        int cnt=0;
        for(int j=0;j<=t;j++) vis[j]=0;
        while(height[i]>=x&&i<=n-1)
        {
            if(!vis[belong[sa[i-1]]])
            {
                vis[belong[sa[i-1]]]=1;
                cnt++;
            }
            i++;
        }
        if(!vis[belong[sa[i-1]]])
        {
            vis[belong[sa[i-1]]]=1;
            cnt++;
        }
        if(cnt>=t)
        {
            anspos=sa[i-1];
            return true;
        }
    }
    return false;
}
int main()
{
    int len,n;
    int casee;
    scanf("%d",&casee);
    while(casee--)
    {
        scanf("%d",&t);
        if(t==0) break;
        n=0;
        int pos=30;
        for(int i=0;i<t;i++)
        {
            scanf("%s",str[i]);
            lenn[i]=strlen(str[i]);
            for(int j=0;j<lenn[i];j++)
             {
                 s[n++]=str[i][j]-'A'+1;
                 belong[n-1]=i;
             }
            s[n++]=pos++;
        }
        s[n]=0;
        getsa(s,sa,n,pos);
        getheight(s,n);
        int l=1,r=60,mid;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(check(mid,n)) l=mid+1;
            else r=mid-1;
        }
        if(r<3) printf("no significant commonalities  \n");
        else
        {
            for(int i=anspos;i<anspos+r;i++)
                printf("%c",s[i]-1+'A');
            printf("\n");
        }
    }
    return 0;
}

KMP第九题

HDU2594
本题题意是求既是A串中的前缀又是B串中的后缀的最长长度。
如果我们将AB进行拼接,我们可以发现 next[len1+len2] n e x t [ l e n 1 + l e n 2 ] 即为最长的即使前缀又是后缀的子串,但是这里有一个细节,就是如果这个长度大于 min(len1,len2) m i n ( l e n 1 , l e n 2 ) ,代表这个是拼接之后产生的,是不可取的,所以我们可以运用性质2来不断减少这个长度,直到他满足 len<min(len1,len2) l e n < m i n ( l e n 1 , l e n 2 ) 即可。
HDU2594代码

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stack>
using namespace std ;
const int maxn = 1e6+5;
int Next[maxn];
char str[maxn];
char mo[maxn];
int n1,n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int main()
{
    int cnt;
    while(scanf("%s%s",mo,str)!=EOF)
    {
        n1=strlen(str);
        n2=strlen(mo);
        int tmp=n2;
        for(int i=n2;i<n2+n1;i++)
        {
            mo[i]=str[i-n2];
        }
        n2=n1+n2;
        mo[n2]='\0';
        Next[0]=-1;
        GetNext();
        int ans=Next[n2];
        while(ans>min(tmp,n2-tmp))
        {
            ans=Next[ans];
        }
        if(ans==0)
        {
            printf("0\n");
            continue;
        }
        for(int i=0;i<ans;i++)
            printf("%c",mo[i]);
        printf(" ");
        printf("%d\n",ans);
    }
    return 0;
}

KMP第十题

HDU3336
本题题意为求字符串的每个前缀在整个字符串中的出现次数。
我们想象性质二,如果next[j]对答案有一个贡献,那么这个贡献在j中一定会再贡献一次,而且j为结尾的字符串对于总串产生的贡献只有长度为j的子串,于是我们可以得到转移方程 ans[j]=ans[next[j]]+1 a n s [ j ] = a n s [ n e x t [ j ] ] + 1 ,最后对所有前缀的贡献取和即为答案。
HDU3336代码

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stack>
using namespace std ;
const int maxn = 1e6+5;
int Next[maxn];
char str[maxn];
char mo[maxn];
int dp[maxn];
int n1,n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int kmp()
{
    int cnt=0;
    int i=0,j=0;
    while(i<n1)
    {
        if(j==-1||str[i]==mo[j]) i++,j++;
        else j=Next[j];
        if(j==n2)
        {
            cnt++;
            j=0;
        }
    }
    return cnt;
}
int ans[maxn];
int main()
{
    int cnt;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%s",&n2,mo);
        Next[0]=-1;
        GetNext();
        int ans=0;
        dp[0]=0;
        for(int i=1;i<=n2;i++)
        {
            dp[i]=dp[Next[i]]+1;
            ans=(ans+dp[i])%10007;
        }
        printf("%d\n",ans);
    }
    return 0;
}

KMP第十一题

HDU4300
本题题意比较难读懂,题意为给你一段密文的映射方式和一段密文+明文的字符串,密文是完整的,而明文不一定是完整的,让你添加最少的字符使他变为完整的密文+明文
如果读懂题意,可以考虑给定字符串中密文长度一定是 >=len/2 >= l e n / 2 的,所以我们可以将后半段的字符均按照映射换为密文,然后找到最长的既在前缀中出现又在后缀中出现的子串,根据性质二和第九题的做法,不断递归next直到找到符合条件的 len l e n 然后跳出即可,输出时要注意明文密文的转换
HDU4300代码

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stack>
using namespace std ;
const int maxn = 1e5+5;
int Next[maxn];
char str[maxn];
char str2[maxn];
char mo[maxn];
int dp[maxn];
int mm[30];
int nn[maxn];
int n1,n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int ans[maxn];
int main()
{
    int cnt;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s%s",str,mo);
        strcpy(str2,mo);
        n2=strlen(mo);
        n1=strlen(str);
        for(int i=0;i<n1;i++)
        {
            mm[i]=str[i]-'a';
            nn[str[i]-'a']=i;
        }
        for(int i=0;i<(n2+1)/2;i++)
        {
            mo[i]=nn[mo[i]-'a']+'a';
        }
        Next[0]=-1;
        GetNext();
        int ans=Next[n2];
        while(ans>min((n2+1)/2,n2-(n2+1)/2))
        {
            ans=Next[ans];
        }
        for(int i=0;i<n2-ans;i++)
            printf("%c",str2[i]);
        for(int i=0;i<n2-ans;i++)
            printf("%c",nn[str2[i]-'a']+'a');
        printf("\n");
    }
    return 0;
}

KMP第十二题

HDU1238
多个字符串的最长公共子串,只不过子串可以逆置出现,只要把第八题的做法反过来再找一次就好,直接上代码

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
using namespace std;
const int maxn = 105;
char str[maxn];
string ansstr;
string str2[maxn];
int main()
{
    int n,t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%s",str);
            str2[i]=str;
        }
        int ans=0;
        string tmp=str2[0];
        int tmplen=tmp.size();
        for(int i=0;i<tmplen;i++)
        {
            for(int j=1;i+j<=tmplen;j++)
            {
                int cnt=1;
                string ss=tmp.substr(i,j);
                for(int k=1;k<n;k++)
                {
                    if(str2[k].find(ss)!=-1)
                        cnt++;
                }
                if(cnt==n)
                {
                    if(ans<j)
                    {
                        ans=j;
                        ansstr=ss;
                    }
                    else if(ans==j)
                    {
                        ansstr=min(ansstr,ss);
                    }
                }
            }
        }
        reverse(tmp.begin(),tmp.end());
        for(int i=0;i<tmplen;i++)
        {
            for(int j=1;i+j<=tmplen;j++)
            {
                int cnt=1;
                string ss=tmp.substr(i,j);
                for(int k=1;k<n;k++)
                {
                    if(str2[k].find(ss)!=-1)
                        cnt++;
                }
                if(cnt==n)
                {
                  //  cout<<ss<<" "<<cnt<<endl;
                    if(ans<j)
                    {
                        ans=j;
                        ansstr=ss;
                    }
                    else if(ans==j)
                    {
                        ansstr=min(ansstr,ss);
                    }
                }
            }
        }
        printf("%d\n",ans);
    }
}

KMP第十三题 ##

HDU3374
本题题意为求一个字符串旋转后的所有串中字典序最大和字典序最小分别出现的次数。
搜先我们要了解字符串的最小表示法 o(n) o ( n ) 的时间复杂度求出旋转后字典序最小的起始下标。
最小表示法戳这里字符串最小表示法
了解了最小表示法之后,我们考虑一下,会发现,只有字符串有循环节的时候才会出现旋转后有相同的串出现的情况,所以我们利用性质1判断是否字符串存在循环节,然后利用最小/最大表示法分别求出下标即可
HDU3374代码

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stack>
using namespace std ;
const int maxn = 1e6+5;
int Next[maxn];
char mo[maxn];
int n1,n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int getmin(char *s)
{
    int n=strlen(s);
    int i=0,j=1,k=0,t;
    while(i<n && j<n && k<n)
    {
        t=s[(i+k)%n]-s[(j+k)%n];
        if (!t) k++;
        else
        {
            if (t>0) i+=k+1;
            else j+=k+1;
            if (i==j) j++;
            k=0;
        }
    }
    return i<j?i:j;

}
int getmax(char *s)
    {
        int len = strlen(s);
        int i = 0, j = 1, k = 0;
        while(i < len && j < len && k < len)
        {
            int t = s[(i+k)%len]-s[(j+k)%len];
            if(!t) k++;
            else
            {
                if(t > 0)
                {
                    if(j+k+1 > i) j = j+k+1;
                    else j = i+1;
                }
                else if(i+k+1 > j) i = i+k+1;
                else i = j+1;
                k = 0;
            }
        }
        return i < j ? i : j;
    }
int ans[maxn];
int main()
{
    int cnt;
    while(scanf("%s",mo)!=EOF)
    {
        n2=strlen(mo);
        Next[0]=-1;
        GetNext();
        int pos=getmin(mo);
        int pos2=getmax(mo);
        int tmp=n2-Next[n2];
        if(n2%tmp==0)
        {
            printf("%d %d %d %d\n",pos+1,n2/tmp,pos2+1,n2/tmp);
        }
        else
        {
            printf("%d %d %d %d\n",pos+1,1,pos2+1,1);
        }
    }
    return 0;
}

KMP第十四题

HDU2609
本题为给你n个01串,可以对每个串旋转任意次,求最少出现多少个不同的字符串,我们可以知道,如果两个字符串是可以旋转之后相同的,那么他们的最小表示法一定是相同的,所以我们可以求出所有字符串的最小表示法,然后用一个set去重就好了。
HDU2609代码

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<set>
using namespace std;
int getmin(char *s)
{
    int n=strlen(s);
    int i=0,j=1,k=0,t;
    while(i<n && j<n && k<n)
    {
        t=s[(i+k)%n]-s[(j+k)%n];
        if (!t) k++;
        else
        {
            if (t>0) i+=k+1;
            else j+=k+1;
            if (i==j) j++;
            k=0;
        }
    }
    return i<j?i:j;

}
char str[105];
string tmp;
set<string> s;
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        s.clear();
        for(int i=0;i<n;i++)
        {
            scanf("%s",str);
            tmp=str;
            int pos=getmin(str);
            string pp=tmp.substr(pos)+tmp.substr(0,pos);
            s.insert(pp);
        }
        printf("%d\n",(int)s.size());
    }
    return 0;
}

KMP第十五题

FZU1901
本题题意为求一个长度 P P ,使所有的

iϵ[0,size(s)p1]
满足

S[i]=S[i+P] S [ i ] = S [ i + P ]

本题即为性质2的终极运用,在次重复一遍,kmp的next不断向前递归的过程可以保证对于每一个当前前缀,都有一段后缀与之对应,所以对于每一个next,我们都可以将他与最后的后缀对应作为一组答案。
FZU1901代码

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stack>
using namespace std ;
const int maxn = 1e6+5;
int Next[maxn];
char mo[maxn];
int n1,n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int ans[maxn];
int main()
{
    int cnt=1;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",mo);
        n2=strlen(mo);
        Next[0]=-1;
        int pp=0;
        GetNext();
        int tmp=Next[n2];
       while(tmp!=0)
       {
           ans[pp++]=tmp;
           tmp=Next[tmp];
       }
        printf("Case #%d: %d\n",cnt++,pp+1);
        for(int i=0;i<pp;i++)
            printf("%d ",n2-ans[i]);
        printf("%d\n",n2);
    }
    return 0;
}

KMP第十六题

UVA11475
本题题意是给你一个字符串,求出最少拼接字符数使其变为回文串
通过思考我们可以发现,只有作为后缀的回文串对结果产生贡献,那么这个题就转变为了找最长的后缀回文子串,我们将字符串倒置再拼接上原串,求得的next[len]也就是倒置字符串的前缀与原字符串的后缀的最大匹配长度,也就是本题答案。
UVA11475代码

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std ;
const int maxn = 1e6+5;
int Next[maxn];
char mo[maxn];
char str[maxn];
int n2;
void GetNext()
{
    int i=0,j=-1;
    while(i<n2)
    {
        if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}
        else j=Next[j];
    }
    return ;
}
int main()
{
    while(scanf("%s",str)!=EOF)
    {
        n2=0;
        int len=strlen(str);
        for(int i=len-1;i>=0;i--)//字符串倒置
            mo[n2++]=str[i];
        mo[n2++]='#';//拼接符
        for(int i=0;i<len;i++)
            mo[n2++]=str[i];//拼接
        mo[n2]='\0';
        Next[0]=-1;
        GetNext();
        int tmp=Next[n2];
        printf("%s",str);
        for(int i=len-1-tmp;i>=0;i--) printf("%c",str[i]);
        printf("\n");
    }
    return 0;
}

未完待续…

  • 11
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值