之前集训的时候曾经自学过KMP,好像就把模板打了就走了,没有深入理解KMP,今天借着老师讲解,重新加强巩固了一番,仅以此篇纪念。
声明:此篇博客不是教学,仅作为之后复习的资料。
KMP解决的是字符串匹配问题,当询问一个较短串在一个较长串中出现的次数及位置时,就可以用KMP算法了。
模板
给定两个由小写字母构成的字符串 L 和 S 。
请你从左到右,找出子串 L 在母串 S 中每次出现的开始位置(匹配位置)。
#include<bits/stdc++.h>
using namespace std;
char s[1000005],t[1000005];
int Next[1000005];
int lens,lent;
void getNext() //计算Next数组
{
int j=1,k=0;
Next[1]=0;
while(j<lent)
{
if(k==0||t[j]==t[k])
Next[++j]=++k;
else k=Next[k];
}
}
int main()
{
scanf("%s",s+1); lens=strlen(s+1); //从下标1开始存储字符串s
scanf("%s",t+1); lent=strlen(t+1); //从下标1开始存储字符串t
getNext(); //计算Next数组
int i=1,j=1,f=0;
while(i<=lens)
{
if(j==0||s[i]==t[j]) i++,j++;
else j=Next[j]; //t串指针j跳转到Next[j]继续匹配
if(j>lent) //找到匹配位置
{
cout<<i-lent<<endl;
i--; //更新i,继续匹配
j=Next[j-1]; //更新j,继续匹配
f=1; //匹配的标记
}
}
if(f==0) cout<<"NO"<<endl; //找不到匹配位置
return 0;
}
常数优化
其实没啥用,还会阻碍KMP的其他用法。但对于某些常熟大的人来说,还是必要的… …
void getNext()
{
int j=1,k=0;
Next[1]=0;
while(j<lent)
{
if(k==0||t[j]==t[k])
{
j++;
k++;
if(t[j]==t[k]) Next[j]=Next[k]; //优化
else Next[j]=k;
}
else k=Next[k];
}
}
最长公共前后缀问题
因为 n x t nxt nxt[ ]数组维护的是 s [ 1 ] s[1] s[1]~ s [ l e n − 1 ] s[len-1] s[len−1]( s [ l e n ] s[len] s[len])的最长公共前后缀,所以可以解决此类问题。
一道板子:
HDU2596 Simpsons’Hidden Talents
这道题唯一需要动脑子的就是要想到将两个串拼接在一起。其余用KMP的 g e t n x t ( ) getnxt() getnxt()函数计算就可以了。
#include<bits/stdc++.h>
using namespace std;
char s[100005];
int len,len1,nxt[100005];
void getnxt()
{
for(int i=2,j=0; i<=len; i++)
{
while(j&&s[j+1]!=s[i]) j=nxt[j];
if(s[j+1]==s[i]) ++j; nxt[i]=j;
}
}
int main()
{
while(scanf("%s",s+1)!=EOF)
{
len1=strlen(s+1);
scanf("%s",s+len1+1);
len=strlen(s+1);
memset(nxt,0,sizeof(nxt));
getnxt();
if(!nxt[len]){puts("0");continue;}
for(int i=1;i<=nxt[len];i++) cout<<s[i];
cout<<" ";
printf("%d\n",nxt[len]);
}
return 0;
}
求最长循环节问题
又一道板子:
#include<bits/stdc++.h>
using namespace std;
int len,nxt[1000005];
char s[1000005];
void getnxt()
{
for(int i=2,j=0;i<=len;i++)
{
while(j&&s[i]!=s[j+1]) j=nxt[j];
if(s[j+1]=s[i]) j++;nxt[i]=j;
}
}
int main()
{
while(scanf("%s",s+1),s[1]!='.')
{
len=strlen(s+1);
memset(nxt,0,sizeof(nxt));
getnxt();
int ans=1;
if(len%(len-nxt[len])==0) ans=len/(len-nxt[len]);
printf("%d\n",ans);
}
return 0;
}
详解见博客:https://blog.csdn.net/deritt/article/details/50765974
撒花完结!!!