这里放一些KMP的题解。
caioj1457
【题意】
我们定义两个字符串
a
a
a和
b
b
b$的乘法:
a
∗
b
a*b
a∗b ,就是把它们连接起来。比如:
a
a
a = “
a
b
c
abc
abc” ,
b
b
b= "
d
e
f
"
def"
def" ,那么 a
∗
*
∗b = “abcdef”.
由此推广,字符串的幂运算:
a
0
a^0
a0 = “” (空字符串)
a
(
n
+
1
)
=
a
∗
(
a
n
)
a^{(n+1)} = a*(a^n)
a(n+1)=a∗(an).
给一个字符串
s
s
s,假设存在
a
n
=
s
a^n=s
an=s,求
n
n
n的最大值。
【输入格式】
多组数据,每组数据一行字符串,长度在
1
1
1和
1000000
1000000
1000000之间。句号"."表示结束。
【输出格式】
每组数据一行,一个整数,输出最大的
n
n
n。就是
s
s
s =
a
n
a^n
an ,
a
a
a是
s
s
s的一个子串.
【样例输入】
abcd
aaaa
ababab
.
【样例输出】
1
4
3
思路
记得KMP的定义吗?
n e x t [ i ] next[i] next[i]表示"前缀子串"与“ i i i结尾的非前缀子串”的最长匹配长度。
即 A [ 1 ∼ n e x t [ i ] ] = A [ i − n e x t [ i ] + 1 ∼ i ] A[1\sim next[i]]=A[i-next[i]+1\sim i] A[1∼next[i]]=A[i−next[i]+1∼i]
根据这个性质, A [ 1 ∼ n e x t [ n ] ] = A [ n − n e x t [ n ] + 1 ∼ n ] A[1\sim next[n]]=A[n-next[n]+1\sim n] A[1∼next[n]]=A[n−next[n]+1∼n],
若
n
−
n
e
x
t
[
n
]
<
n
e
x
t
[
n
]
n-next[n]<next[n]
n−next[n]<next[n]。
设
w
=
n
−
n
e
x
t
[
n
]
w=n-next[n]
w=n−next[n]则
A
[
1
∼
w
]
=
A
[
w
+
1
∼
2
∗
w
]
A[1\sim w]=A[w+1\sim 2*w]
A[1∼w]=A[w+1∼2∗w]
自己找规律就好了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
const int N=1e6+10;
int nxt[N];char s[N];
int main()
{
while(scanf("%s",s+1)!=EOF)
{
if(s[1]=='.')break;
int n=strlen(s+1);
nxt[1]=0;
for(int i=2,j=0;i<=n;i++)
{
while(j&&s[j+1]!=s[i])j=nxt[j];
if(s[j+1]==s[i])++j;
nxt[i]=j;
}
printf("%d\n",n%(n-nxt[n])==0?n/(n-nxt[n]):1);
}
return 0;
}
caioj1458
【题意】
给一个字符串,如果在前 i 位置处满足连续循环A^K(A:单位循环段,K:循环个数),
则输出i和K(仅输出K>1的情况,按i的递增顺序)
【输入格式】
一行一个字符串。字符串的长度为N (2 <= N <= 1 000 000)
【输出格式】
若干行的i和k。
【样例1输入】
aaa
【样例1输出】
2 2
3 3
【样例2输入】
aabaabaabaab
【样例2输出】
2 2
6 2
9 3
12 4
思路
和上一题一毛一样。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
const int N=1e6+10;
void qw(int x)
{
if(x<0)x=-x,putchar('-');
if(x/10)qw(x/10);
putchar(x%10+48);
}
char s[N];int nxt[N];
int main()
{
scanf("%s",s+1);
int n=strlen(s+1);
nxt[1]=0;
for(int i=2,j=0;i<=n;i++)
{
while(j&&s[j+1]!=s[i])j=nxt[j];
if(s[j+1]==s[i])++j;nxt[i]=j;
if(nxt[i])
{
bool bk=i%(i-nxt[i]);
if(!bk)qw(i),putchar(' '),qw(i/(i-nxt[i])),puts("");
}
}
return 0;
}
caioj1459
【题意】
给出一个字符串S. 1 <= S的长度 <= 400000.
找出所有S的前缀等于后缀的情况。按长度递增输出长度。相互之间用空格隔开。
【输入格式】
多组数据。每组数据一行输入S字符串(一定要使用 while( scanf("%s")!=EOF ) )
【输出格式】
每组数据一行。从小到大输出S的前缀等于后缀的长度。
【样例输入】
ababcababababcabab
aaaaa
【样例输出】
2 4 9 18
1 2 3 4 5
样例1解释
ababcababababcabab
ab
ab:2
abab
abab:4
ababcabab
ababcabab:9(正中间隔开)
ababcababababcabab
ababcababababcabab:18(全部)
样例2解释:
aaaaa
a
a:1
aa
aa:2
aaa
aaa:3
aaaa
aaaa:4
aaaaa
aaaaa:5
思路
还记得引理吗?
n e x t [ n ] next[n] next[n]的候选项都来自于 n e x t [ n e x t [ n ] ] ⋯ ⋯ next[next[n]]\cdots\cdots next[next[n]]⋯⋯,倒序输出所有 n e x t [ n ] next[n] next[n]的候选项即可。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
const int N=1e6+10;
void qw(int x)
{
if(x<0)x=-x,putchar('-');
if(x/10)qw(x/10);
putchar(x%10+48);
}
char s[N];int nxt[N],ans[N],len;
int main()
{
//freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
while(scanf("%s",s+1)!=EOF)
{
int n=strlen(s+1);
nxt[1]=0;
for(int i=2,j=0;i<=n;i++)
{
while(j&&s[j+1]!=s[i])j=nxt[j];
if(s[j+1]==s[i])++j;nxt[i]=j;
}
ans[1]=n;len=1;
int x=nxt[n];
while(nxt[x])ans[++len]=x,x=nxt[x];
if(x)ans[++len]=x;
for(int i=len;i>=1;i--)qw(ans[i]),putchar(' ');
puts("");
}
return 0;
}
caioj1460
题目描述】
给出两个字符串sa和sb,求出sa能在sb中匹配的最大次数。
【输入格式】
第一行一个整数n,表示下来有n组数据。
下来每组数据有两行,分别是字符串sa和字符串sb,都为大写字母。 1 ≤ |sa|≤1000000,|sa| ≤ |sb| ≤ 1000000.
|sa|,|sb|表示字符串sa和sb的长度。
【输出格式】
每组数据输出一个整数,表示最大的匹配次数。
【样例输入】
3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN
【样例输出】
1
3
0
思路
和模版没啥区别
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int N=1e6+10;
const int M=1e6+10;
int nxt[N],n,m;//next居然是系统函数
char a[N],b[M];
void kmp()
{
nxt[1]=0;int ans=0;
for(int i=2,j=0;i<=n;i++)
{
while(j&&a[i]!=a[j+1])j=nxt[j];//j++
if(a[i]==a[j+1])++j;nxt[i]=j;
}
for(int i=1,j=0;i<=m;i++)
{
while(j&&b[i]!=a[j+1])j=nxt[j];
if(a[j+1]==b[i])++j;
if(j==n){++ans;j=nxt[j];}
}
printf("%d\n",ans);
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%s%s",a+1,b+1);
n=strlen(a+1),m=strlen(b+1);
kmp();
}
return 0;
}