写在前面:太弱了,现在才开始做kmp← ←
传送门-P1961
题意:找出字符串中所有循环节次数大于1的前缀子串,输出它们的最小循环节长度与最大循环次数
思路:首先我们要学一门姿势
如果对于next数组中的 i, 符合 i % ( i - next[i] ) == 0 && next[i] != 0 ,
则说明字符串循环,而且 循环节长度为: i - next[i] 循环次数为: i / ( i - next[i] )
传送门-姿势及证明
接下来就比较简单了,预处理next数组,然后1-n找对应的前缀子串的循环节次数,大于1就输出答案(实际上就是next[i]>0,使循环节不是它本身)
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int now,id,n,next[1000010];
char s[1000010];
main()
{
scanf("%d",&n);
while (n)
{
scanf("%s",s);
printf("Test case #%d\n",++id);
for (int i=1;i<n;i++)
{
now=next[i];
while (now&&s[now]!=s[i]) now=next[now];
next[i+1]=now+(s[now]==s[i]);
}
for (int i=1;i<=n;i++)
if (next[i]&&i%(i-next[i])==0) printf("%d %d\n",i,i/(i-next[i]));
puts("");
scanf("%d",&n);
}
}
传送门-P2406
题意:给定一个字符串,求它的最大循环次数
思路:比1961还简单,因为不用求非全串的子串,只用求全串,处理方法类似,只是次数为1的时候也要输出
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
int now,l,next[1000010];
char s[1000010];
void work()
{
if (s[0]=='.') exit(0);
l=strlen(s);
for (int i=1;i<l;i++)
{
now=next[i];
while (now&&s[now]!=s[i]) now=next[now];
next[i+1]=now+(s[i]==s[now]);
}
if (next[l]==0||l%(l-next[l])) printf("1\n");
else printf("%d\n",l/(l-next[l]));
}
main()
{
while (scanf("%s",s)!=EOF)
work();
}
传送门-P2752
题意:给定一个字符串,求出它所有相同的前缀和后缀
思路:处理出next数组后,从next[len]输出,并当前指针跳到next[len],因为next[i]存的就是长度为i的串中前缀和后缀相等的最大值
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
char s[400010];
int l,next[400010],ans[400010];
void work()
{
l=strlen(s);
int now;
for (int i=1;i<l;i++)
{
now=next[i];
while (now&&s[i]!=s[now]) now=next[now];
next[i+1]=now+(s[i]==s[now]);
}
ans[0]=0;
ans[++ans[0]]=l;
now=l;
while (next[now]) ans[++ans[0]]=next[now],now=next[now];
for (int i=ans[0];i>=1;i--) printf("%d%c",ans[i]," \n"[i==1]);
}
main()
{
while (scanf("%s",s)!=EOF)
work();
}
传送门-P3461
题意:给定两个字符串,找出第一个字符串在第二个字符串中出现次数
思路:用next数组进行字符串匹配,如果匹配成功,ans++,当前指针返回第一个字符串的最后一位
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
char ch[10010],s[1000010];
int t,ans,l1,l2,next[10010];
main()
{
scanf("%d",&t);
while (t--)
{
ans=0;
scanf("%s%s",ch,s);
l1=strlen(ch);l2=strlen(s);
int now;
for (int i=1;i<l1;i++)
{
now=next[now];
while (ch[i]!=ch[now]&&now) now=next[now];
next[i+1]=now+(ch[i]==ch[now]);
}
now=0;
for (int i=0;i<l2;i++)
{
while (ch[now]!=s[i]&&now) now=next[now];
now+=(ch[now]==s[i]);
if (now==l1) ans++,now=next[now];
}
printf("%d\n",ans);
}
}
认真学习算法并尽力理解其本质,是学好OI的基础