长为n-next[n]的前缀必为覆盖子串。
长为n-next[n]的前缀是最短的。
POJ 3461 Oulipo
计算单词W在整篇文章T中出现的次数。
KMP最基本的应用,统计出现次数,套模板即可。
http://poj.org/problem?id=3461 if(j==m){
// TO DO:
ans++;
j=f[j];
}
POJ 2752 Seek the Name, Seek the Fame
这道题目要求既是前缀字符串又是后缀字符串的所有可能的长度,正好运用了KMP算法里面next数组的意义,要保证结果是针对整个字符串的,所以在next数组中从后往前扫描即可,因为,next数组中,越往后,数字就越大,并且表示的是后缀和前缀相同的最大长度,所以所得到的结果顺序是反的,要求从小到大输出,逆序输出就可以了。
http://poj.org/problem?id=2752
POJ 2406 Power Strings
输出最大的n使得s由a重复n次而成。
当 n%(n-f[n])==0时,n-f[n] 是s最短的循环节。
为什么呢? 假设我们有一个字符串ababab,那么next[6]=4对吧,由于next的性质是,匹配失败后,下一个能继续进行匹配的位置,也就是说,把字符串的前四个字母,abab,平移2个单位,这个abab一定与原串的abab重合(否则就不满足失败函数的性质),这说明了什么呢,由于字符串进行了整体平移,而平移后又要重叠,那么必有
s[1]=s[3],s[2]=s[4],s[3]=s[5],s[4]=s[6].说明长度为2的字符串在原串中一定重复出现,这就是len-next[len]的含义!
(注:以上论述内容转自网络,不过说的的确很精到)
http://poj.org/problem?id=2406 if (n%(n-f[n])==0){
printf("%d\n",n/(n-f[n]));
}
else{
printf("1\n");
}
POJ 1961 Period
对每个前缀i,若能由某些字符重复k次形成,输出最大的k。
与上题类似,枚举i,若i%(i-f[i])==0 则最短循环节为i-f[i],k为i/(i-f[i])
for (int i=2;i<=n;i++){
if (f[i]>0&&i%(i-f[i])==0){
printf("%d %d\n",i,i/(i-f[i]));
}
}
http://poj.org/problem?id=1961
HDU 3336 Count the string
求出s有多少个子串是它本身的前缀。
DP公式如下。
http://acm.hdu.edu.cn/showproblem.php?pid=3336
for (int i=1;i<=n;i++){
dp[i]=dp[f[i]]+1;
ans=(ans+dp[i])%10007;
}
HDU 3746 Cyclic Nacklace
至少要在字符串s后面补几个字符才能凑成一个循环。
若本身已经有循环节,则答案为0。
http://acm.hdu.edu.cn/showproblem.php?pid=3746
if (f[n]>0&&n%(n-f[n])==0) printf("0\n");
else printf("%d\n",n-f[n]-n%(n-f[n]));
/*
POJ 3461
*/
#include<cstdio>
#include<cstring>
using namespace std;
char c[10005],s[1000005];
int next[10005];
int ans;
void getnext(char *p,int *next){
int i=0,j=-1;
int len=strlen(p);
next[0]=-1;
while(i<len){
if(j==-1 || p[i]==p[j]){
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int kmp(char *s,char *p,int *next){
int i=0,j=0;
ans=0;
int n=strlen(s);
int m=strlen(p);
getnext(p,next);
while(i<n){
if(j==-1 || s[i]==p[j]){
i++;
j++;
}
else
j=next[j];
if(j==m){
ans++;// 记录符合的次数
j=next[j];
}
}
return ans;
}
int main(){
int test;
scanf("%d",&test);
while(test--){
scanf("%s%s",c,s);
printf("%d\n",kmp(s,c,next));
}
}
/*
POJ 2752
*/
#include<cstdio>
#include<cstring>
using namespace std;
char s[400005];
int next[400005],a[400005];
void getnext(char *s,int *next){
int i=0,j=-1,len=strlen(s);
next[0]=-1;
while(i<len){
if(j==-1 || s[i]==s[j]){
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main(){
while(~scanf("%s",s)){
getnext(s,next);
int len=strlen(s),i,k=0;
i=len;
a[0]=len; //加入字符串本身,是最大的
while(next[i]>0){
i=next[i];
a[++k]=i;
}
for(int j=k;j>=0;j--){
printf("%d",a[j]);
if(j)
printf(" ");
}
printf("\n");
}
}
/*
POJ 2046
*/
#include<cstdio>
#include<cstring>
int next[1000100];
char s[1000100];
void get_next(char *s,int *next){
int i=0,j=-1;
next[0]=-1;
int len=strlen(s);
while(i<len){
if(j==-1||s[i]==s[j]){
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main(){
while(~scanf("%s",s)){
if(s[0]=='.')
break;
get_next(s,next);
int len=strlen(s);
if(len%(len-next[len])==0)
printf("%d\n",len/(len-next[len]));
else
printf("1\n");
}
}
/*
求前n位字符中循环字符串循环的次数
poj 1961
*/
#include<cstdio>
#include<cstring>
const int maxn=1000100;
int next[maxn];
char s[maxn];
void get_next(char *s,int *next){
int i=0,j=-1,len=strlen(s);
next[0]=-1;
while(i<len){
if(j==-1 || s[i]==s[j]){
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main(){
int len,t,cas=1;
while(scanf("%d",&len)&&len){
scanf("%s",s);
get_next(s,next);
printf("Test case #%d\n",cas++);
for(int i=2;i<=len;i++){
t=i-next[i];
if((i/t>1)&&i%t==0)
printf("%d %d\n",i,i/t);
}
printf("\n");
}
}
/*
HDU 3336
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define MAXN 300000
int n,m,dp[MAXN],next[MAXN];
char s[MAXN];
void get_next(char *s,int *next){
int i=0,j=-1,len=strlen(s);
next[0]=-1;
while(i<len){
if(j==-1||s[i]==s[j]){
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main(){
scanf("%d",&n);
while(n--){
int sum=0;
scanf("%d",&m);
scanf("%s",s);
get_next(s,next);
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;i++){
dp[i]=(dp[next[i]]+1)%10007;
sum=(sum+dp[i])%10007;
}
printf("%d\n",sum);
}
}
/*
HDU 3746
长为n-next[n]的前缀必为覆盖子串。
长为n-next[n]的前缀是最短的。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int next[100001];
char c[100001];
void get_next(char *s,int *next){
int j=-1,i=0,len=strlen(s);
next[0]=-1;
while(i<len){
if(j==-1||s[i]==s[j]){
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main(){
int test;
scanf("%d",&test);
while(test--){
scanf("%s",c);
len=strlen(c);
get_next(c,next);
k= len-next[len];//最短的前缀
if(k!=len&&len%k==0 )//循环节必须大于2
printf("0\n");
else
printf("%d\n",k-len%k);
}
}
/*
求前n位字符中循环字符串循环的次数
poj 1961
*/
#include<cstdio>
#include<cstring>
const int maxn=1000100;
int next[maxn];
char s[maxn];
void get_next(char *s,int *next){
int i=0,j=-1,len=strlen(s);
next[0]=-1;
while(i<len){
if(j==-1 || s[i]==s[j]){
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main(){
int len,t,cas=1;
while(scanf("%d",&len)&&len){
scanf("%s",s);
get_next(s,next);
printf("Test case #%d\n",cas++);
for(int i=2;i<=len;i++){
t=i-next[i];
if((i/t>1)&&i%t==0)
printf("%d %d\n",i,i/t);
}
printf("\n");
}
}
/*
HDU 3336
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define MAXN 300000
int n,m,dp[MAXN],next[MAXN];
char s[MAXN];
void get_next(char *s,int *next){
int i=0,j=-1,len=strlen(s);
next[0]=-1;
while(i<len){
if(j==-1||s[i]==s[j]){
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main(){
scanf("%d",&n);
while(n--){
int sum=0;
scanf("%d",&m);
scanf("%s",s);
get_next(s,next);
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;i++){
dp[i]=(dp[next[i]]+1)%10007;
sum=(sum+dp[i])%10007;
}
printf("%d\n",sum);
}
}
/*
HDU 3746
长为n-next[n]的前缀必为覆盖子串。
长为n-next[n]的前缀是最短的。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int next[100001];
char c[100001];
void get_next(char *s,int *next){
int j=-1,i=0,len=strlen(s);
next[0]=-1;
while(i<len){
if(j==-1||s[i]==s[j]){
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main(){
int test;
scanf("%d",&test);
while(test--){
scanf("%s",c);
len=strlen(c);
get_next(c,next);
k= len-next[len];//最短的前缀
if(k!=len&&len%k==0 )//循环节必须大于2
printf("0\n");
else
printf("%d\n",k-len%k);
}
}