题目:POJ1961.
题目大意:给定一个长度为
n
n
n的字符串
S
[
1..
n
]
S[1..n]
S[1..n],求出对于每一个前缀
S
[
1..
i
]
S[1..i]
S[1..i]满足最大循环次数大于
1
1
1,该前缀的长度与最大循环次数.
1
≤
n
≤
1
0
6
1\leq n\leq 10^6
1≤n≤106.
注:一个串 S [ 1.. n ] S[1..n] S[1..n]的一个 i i i最小的前缀 S [ 1.. i ] S[1..i] S[1..i]满足 S [ 1.. i ] S[1..i] S[1..i]重复 k ( k ∈ N + ) k(k\in N_+) k(k∈N+)次与 S [ 1.. n ] S[1..n] S[1..n]完全相同,那么 S [ 1.. i ] S[1..i] S[1..i]被称为 S [ 1.. n ] S[1..n] S[1..n]的最小循环元, k k k被称为 S [ 1.. n ] S[1..n] S[1..n]的最大循环次数.
最小循环元问题是KMP的一个很经典的应用,所以先求出原串的 n e x t next next数组.
我们回忆 n e x t next next数组的含义, n e x t next next数组表示的是最长的一个既是前缀又是后缀的真子串的长度.那么应用这个数组的性质,容易猜到一个结论:当 ( i − n e x t [ i ] ) ∣ i (i-next[i])|i (i−next[i])∣i时, S [ 1.. i ] S[1..i] S[1..i]就有最小循环元 S [ 1.. i − n e x t [ i ] ] S[1..i-next[i]] S[1..i−next[i]].
我们可以证明一下:
首先证明 S [ 1.. i − n e x t [ i ] ] S[1..i-next[i]] S[1..i−next[i]]是 S [ 1.. i ] S[1..i] S[1..i]的一个循环元.
我们发现,若 ( i − n e x t [ i ] ) ∣ i (i-next[i])|i (i−next[i])∣i时,当 n e x t [ i ] = 0 next[i]=0 next[i]=0时结论显然成立.
当
n
e
x
t
[
i
]
>
0
next[i]>0
next[i]>0也就是
i
−
n
e
x
t
[
i
]
<
i
i-next[i]<i
i−next[i]<i时,必然会出现这样的情况:
黑色的表示一个串
A
A
A,长度为
S
S
S,红色的这两段相等,也就是说红色的长度就是
n
e
x
t
[
S
]
next[S]
next[S],那么显然蓝色的线段都必须相等.
于是蓝色的长度就为 S − n e x t [ S ] S-next[S] S−next[S],发现上面的结论成立.
同时由于 n e x t next next数组的极大性,发现这个循环元一定是最小的,所以整个结论成立.
有了这个结论的成立,我们就可以开始愉快的写代码了:
#include<iostream>
#include<cstdio>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=1000000;
char a[N+9];
int n,nxt[N+9],top;
char rc(){
char c=getchar();
for (;c<97||c>126;c=getchar());
return c;
}
void self_mate(){
int j=0;
nxt[1]=0;
for (int i=2;i<=n;++i){
while (a[j+1]^a[i]&&j>0) j=nxt[j];
if (a[j+1]==a[i]) ++j;
nxt[i]=j;
}
}
Abigail into(){
for (int i=1;i<=n;++i)
a[i]=rc();
}
Abigail work(){
self_mate();
}
Abigail outo(){
printf("Test case #%d\n",++top);
for (int i=1;i<=n;++i)
if (i%(i-nxt[i])==0&&i/(i-nxt[i])>1)
printf("%d %d\n",i,i/(i-nxt[i]));
puts("");
}
int main(){
while (~scanf("%d",&n)&&n){
into();
work();
outo();
}
return 0;
}