写写我对kmp的理解吧,kmp算法就是对于两个串的匹配,一个文本串S,一个模式串T,用T来匹配S
对于kmp算法的关键就是next数组的解读了,网上博客千万,各有观点吧,首先说一下我理解的next数组,next数组里面存入的值比如 next[3]=1;就是这个串abacdedf(比如说)next[3]就是代表aba这个串相同的前缀后缀,这里插入一下前缀后缀:
( 例如字符串:abcdab 它的前缀是,a,ab,abc,abcd,abcda; 它的后缀是:b,ab,dab,cdab,bcdab; 这个就是所谓的前缀和后缀)、
当模式串T与主串S匹配失败时候,模式串T返回到next [ j ](next [ j ] 代表下标为几,这里的字符串规定下标从0开始的)通过用next数组来减少没必要的匹配次数,模式串在下标为j 失配了,此时的next
[ j ]说的是下标,比如next[ 3 ]=1;就是返回到模式串下标为1的位置,也就是模式串的第二个,这里的next [ j ]的数值就是代表 文本串的 i(文本串是在i处失配的)文本串中 i 指针前的 next[ j ]位和模式串的0到 next [ j ]是完全相同的;
在这里我想说一下kmp这个模板
void kmp(int lena,int lenb)
{
int i=0,j=0;
getnext(b);
while(i<lena&&j<lenb)
{
if(j==-1||a[i]==b[j])
{
i++,j++;
}
else
j=nextt[j];
/**************************/到此不管结果匹配如何 此时的j是几就代表从模式串的开头匹配了几个字符,通俗来讲就是此时j是几就代表匹配了几个字符,例如如果模式串就第一个和文本串有一样的,不就是执行第一个if语句,j不就+1了,不就是从零到一了
if(j==lenb)
{
********//内容
}
}
}
说了这么多,还是看题目吧;
**题目链接:**https://vjudge.net/contest/315202#problem/A
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=100005;
#define pi acos(-1.0)
using namespace std;
int n,m;
char nextt[100];
int total,lent;
char key[100];
void getnext(char *t)
{
nextt[0]=-1;
lent=strlen(t);
int k=-1,j=0;
while(j<lent)
{
if(k==-1||t[k]==t[j])
{
k++,j++,nextt[j]=k;
}
else
k=nextt[k];
}
}
int main()
{
sf("%d %d",&n,&m);
sf("%s",key);
getnext(key);
total=0;
int j=0;
while(total<m)
{
printf("%c",key[j]);
j++;
if(j==n)
{
total++;
j=nextt[n]; //为什么要返回next[n],next[n]是几,就代表模式串从下标为0开始到next[n]-1和文本串完全相同,也就是没用重复的了;
}
}
return 0;
}
题目链接:https://vjudge.net/contest/315202#problem/B
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1000005;
#define pi acos(-1.0)
using namespace std;
int t,n,m;
int a[maxn],b[maxn];
int ans,lena,lenb;
int nextt[maxn];
void getnext(int *t)
{
nextt[0]=-1;
int k=-1,j=0;
while(j<lenb)
{
if(k==-1||t[k]==t[j])
{
k++,j++,nextt[j]=k;
}
else
k=nextt[k];
}
}
void kmp(int lena,int lenb)
{
int i=0,j=0;
getnext(b);
while(i<lena&&j<lenb)
{
if(j==-1||a[i]==b[j])
{
i++,j++;
}
else
j=nextt[j];
if(j==lenb)
{
ans=min(ans,i-j+1);
//printf("yes\n");
j=0;
}
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
ans=inf;
scanf("%d %d",&lena,&lenb);
for(int i=0;i<lena;i++)
sf("%d",&a[i]);
for(int i=0;i<lenb;i++)
sf("%d",&b[i]);
kmp(lena,lenb);
if(ans!=inf)
printf("%d\n",ans);
else
printf("-1\n");
}
return 0;
}
这个题目和第一个很像,就是返回 j=next[lent];
题目链接:https://vjudge.net/contest/315202#problem/C
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1e6+50;
#define pi acos(-1.0)
using namespace std;
int t,n,m;
char a[10005],b[1000005];
int ans,lena,lenb;
int nexts[20005];
void getnext(char *p)
{
int k=-1,j=0;
nexts[0]=-1;
while(j<strlen(p))
{
if(k==-1||p[k]==p[j])
{
k++,j++,nexts[j]=k;
}
else
k=nexts[k];
}
}
void kmp()
{
int i=0,j=0,x=0;
while(j<lena&&i<lenb)
{
if(j==-1||a[j]==b[i])
{
i++,j++;
}
else
j=nexts[j];
if(j==lena)
{
ans++;
j=nexts[j];
}
}
}
int main()
{
sf("%d",&t);
while(t--)
{
ans=0;
scanf("%s%s",a,b);//a是单词,b是文本
lena=strlen(a);
lenb=strlen(b);
getnext(a);
kmp();
printf("%d\n",ans);
}
return 0;
}
题目链接:https://vjudge.net/contest/315202#problem/D
这个题目不用返回next[lens],让j=0,比较简单;
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1005;
#define pi acos(-1.0)
using namespace std;
int nextt[maxn];
char s[maxn],t[maxn];
int ans,lent,lens;
void getnext(char *t,int *nextt)
{
nextt[0]=-1;
int j=0,k=-1;
while(j<lent)
{
if(k==-1||t[j]==t[k])
{
k++,j++;
nextt[j]=k;
}
else
k=nextt[k];
}
}
void kmp(int lens,int lent)
{
int i=0,j=0;
while(i<lens&&j<lent)
{
if(j==-1||s[i]==t[j])
{
i++,j++;
}
else
j=nextt[j];
if(j==lent)
{
ans++;
j=0;
}
}
}
int main()
{
while(sf("%s",s))
{
if(s[0]=='#') break;
scanf("%s",t);
lens=strlen(s),lent=strlen(t);
ans=0;
getnext(t,nextt);
kmp(lens,lent);
cout<<ans<<endl;
}
return 0;
}
题目链接 :https://vjudge.net/contest/315202#problem/E
开始循环节了,循环节长度就是等于:下标-next[下标]; 下标从一开始
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1000000+5;
#define pi acos(-1.0)
using namespace std;
char key[maxn];
int nextt[maxn];
int n,k,lens,flag;
void getnext(char *p)
{
int k=-1,j=0;
nextt[0]=-1;
lens=strlen(p);
while(j<lens)
{
if(k==-1||p[k]==p[j])
{
k++,j++,nextt[j]=k;
}
else
k=nextt[k];
}
}
int main()
{
flag=0;
while(scanf("%d",&n)&&n)
{
sf("%s",key);
printf("Test case #%d\n",++flag);
memset(nextt,0,sizeof(nextt));
getnext(key);
for(int i=1;i<=n;i++)
{
if(!nextt[i])
continue;
k=i-nextt[i];
if(i%k==0)
printf("%d %d\n",i,i/k);//前缀大小,循环次数
}
printf("\n");
}
return 0;
}
题目链接:https://vjudge.net/contest/315202#problem/G
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=100000+5;
#define pi acos(-1.0)
using namespace std;
int nextt[maxn];
int t,lens;
char key[maxn];
void getnext()
{
int j=0,k=-1;
nextt[0]=-1;
lens=strlen(key);
while(j<lens)
{
if(k==-1||key[j]==key[k])
{
k++,j++,nextt[j]=k;
}
else k=nextt[k];
}
}
int main()
{
sf("%d",&t);
while(t--)
{
int ans;
lens=strlen(key);
sf("%s",key);
getnext();
int k=lens-nextt[lens];//循环结的长度
if(k!=lens&&lens%k==0) ans=0;//防止abcde
else
ans=k-nextt[lens]%k;//abcab去掉ab
printf("%d\n",ans);
}
return 0;
}
题目链接:https://vjudge.net/contest/315202#problem/H
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1000000+5;
#define pi acos(-1.0)
using namespace std;
int nextt[maxn];
char s[maxn],ans[maxn],t[maxn];
int m,x,y,lens,lent,flag,xx;
void getnext()
{
int k=-1,j=0;
nextt[0]=-1;
while(j<lent) //(t[j])
{
if(k==-1||t[k]==t[j])
{
k++,j++,nextt[j]=k;
}
else
k=nextt[k];
}
}
void kmp()
{
int i=0,j=0;
while(i<y-x+1)
{
if(j==-1||ans[i]==t[j])
{
i++,j++;
}
else
j=nextt[j];
if(j==lent)
{
//printf("yes\n");
xx++,j=nextt[j];
}
}
}
int main()
{
scanf("%d %d %d",&lens,&lent,&m);
sf("%s%s",s,t);
while(m--)
{
xx=0;
scanf("%d %d",&x,&y);
x=x-1,y=y-1;
if(y-x+1<lent)
{
cout<<0<<endl; continue;
}
flag=0;
//memset(ans,0,sizeof(ans));
for(int i=x;i<=y;i++)
ans[flag++]=s[i];
/*for(int i=0;i<=y-x;i++)
ans[i]=s[x-1+i];*/
getnext();
kmp();
printf("%d\n",xx);
}
return 0;
}
拉马龙函数:返回一个字符串最大的回文字串。
题目链接:https://vjudge.net/contest/315202#problem/J
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1000000+5;
#define pi acos(-1.0)
using namespace std;
char begin[maxn<<1],end[maxn<<1];
int len[maxn<<1];
int turn()
{
int lens=strlen(begin);
int now=0;
end[0]='$';
for(int i=0;i<lens;i++)
{
end[++now]='#';
end[++now]=begin[i];
}
end[++now]='#';
return now;
}
int lamalong(int total)
{
int maxx=0;
int mx=0,id=0;
for(int i=1;i<=total;i++)
{
if(i<mx)
{
len[i]=min(mx-i,len[2*id-i]);
}
else
len[i]=1;
while(end[len[i]+i]==end[i-len[i]])
len[i]++;
if(i+len[i]>mx)
{
mx=i+len[i];
id=i;
}
maxx=max(maxx,len[i]);
}
return maxx-1;
}
int main()
{
int t=0;
while(sf("%s",begin)!=EOF)
{
//if(begin[0]=='E') break;
if(strcmp(begin,"END")==0) break;
int total=turn();
//cout<<total<<endl;
int ans=lamalong(total);
printf("Case %d: %d\n",++t,ans);
}
return 0;
}
题目链接:https://vjudge.net/contest/315202#problem/K
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1000000+5;
//const int maxn=3*1e6+66;
#define pi acos(-1.0)
using namespace std;
char beginn[maxn<<1],endd[maxn<<1];
int len[maxn<<1];
int ans,t,lens;
int turnn()
{
memset(endd,0,sizeof(endd));
lens=strlen(beginn);
int now=0;
endd[0]='$';
for(int i=0;i<lens;i++)
{
endd[++now]='#';
endd[++now]=beginn[i];
}
endd[++now]='#';
// cout<<now<<endl;
return now;
}
int lamalong(int total)
{
int maxx=0,mx=0,id=0;
for(int i=1;i<=total;i++)
{
if(i<mx) len[i]=min(mx-i,len[2*id-i]);
else len[i]=1;
while(endd[len[i]+i]==endd[i-len[i]])
len[i]++;
if(i+len[i]>mx)
{
mx=i+len[i],id=i;
//printf("此时 len[i]==%d id==%d,i==%d,mx==%d\n",len[i],id,i,mx);
}
}
if(mx>=total) return len[id]-1;//除去本身 *q* 是2 mx左开右闭
}
int main()
{
int t,k=0;
sf("%d",&t);
while(t--)
{
scanf("%s",beginn);
int total=turnn();
lens=strlen(beginn);
ans=lens+lens-lamalong(total);
if(lens==lamalong(total))
printf("Case %d: %d\n",++k,lens);
else
//printf("lamalong %d\n",lamalong(total));
printf("Case %d: %d\n",++k,ans);
}
return 0;
}
对于字符串的循环节。不止是可以一维的,同时也可以是二维上的,比如一个二维数组让你去求,它的一个循环节,例如题目 poj2185 ;题目链接:https://vjudge.net/contest/315202#problem/M
思路就是求出每一维的循环节长度,然后取最大,结果相乘就是最小覆盖矩阵了。
#include <iostream>
using namespace std;
char s[10005][80];
int nextt[10005];
int r,c,l,h;
int getnextc(int row)
{
int j=0,k=-1;
nextt[0]=-1;
while(j<c)//每行长度
{
if(k==-1||s[row][j]==s[row][k])
j++,k++,nextt[j]=k;
else
k=nextt[k];
}
return c-nextt[c];
}
int getnextr(int col)
{
int j=0,k=-1; nextt[0]=-1;
while(j<r)//每列长度
{
if(k==-1||s[j][col]==s[k][col])
k++,j++,nextt[j]=k;
else
k=nextt[k];
}
return r-nextt[r];
}
int main()
{
while(scanf("%d %d",&r,&c)!=EOF)
{
for(int i=0;i<r;i++)
scanf("%s",&s[i]);
h=0,l=0;
for(int i=0;i<r;i++)
h=max(h,getnextc(i));
for(int i=0;i<c;i++)
l=max(l,getnextr(i));
printf("%d\n",l*h);
}
return 0;
}