NOI2014动物园
题目描述
有一个
n
u
m
num
num数组——对于字符串
S
S
S的前
i
i
i个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作
n
u
m
[
i
]
num[i]
num[i]。
例如
S
S
S为
a
a
a
a
a
aaaaa
aaaaa,则
n
u
m
[
4
]
=
2
num[4]=2
num[4]=2。
这是因为
S
S
S的前
4
4
4个字符为
a
a
a
a
aaaa
aaaa,其中
a
a
a和
a
a
aa
aa都满足性质‘既是后缀又是前缀’,同时保证这个后缀与这个前缀不重叠。同理,
n
u
m
[
1
]
=
0
num[1]=0
num[1]=0,
n
u
m
[
2
]
=
n
u
m
[
3
]
=
1
num[2]=num[3]=1
num[2]=num[3]=1,
n
u
m
[
5
]
=
2
num[5]=2
num[5]=2。
写一个程序求出num数组呢?
∏
(
i
=
1
…
n
)
=
(
n
u
m
[
i
]
+
1
)
∏(i=1…n)=(num[i]+1)
∏(i=1…n)=(num[i]+1) 对
1000000007
1000000007
1000000007取模的结果即可。
输入格式
输入文件的第1行仅包含一个正整数
n
n
n
表示测试数据的组数。 随后
n
n
n行,每行描述一组测试数据。
每组测试数据仅含有一个字符串
S
S
S。数据保证
S
S
S中仅含小写字母。
输出格式
输出文件应包含
n
n
n行
每行描述一组测试数据的答案
样例一
3
aaaaa
ab
abcababc
36
1
32
题解
先用
k
m
p
kmp
kmp求出最大匹配,顺便记下该位置的前后缀匹配个数。
如果把不重叠这个条件去掉,
n
u
m
[
i
]
num[i]
num[i]就是问指针要在
n
e
x
t
next
next数组上跳多少次才能跳到
0
0
0。
假设我们处理第
i
i
i位,一路匹配最终匹配到第
j
j
j位,那么
n
u
m
[
i
]
=
n
u
m
[
j
]
+
1
num[i]=num[j]+1
num[i]=num[j]+1.
要求不重叠,所以先跳到一个不重叠的子串,然后对每个位置求
n
e
x
t
next
next,直到满足前缀长度不超过一半。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e9+7;
const int M = 1e6+10;
ll cnt;
char ch[M];
int next[M], num[M], n, len;
void add(int ai) { cnt *= ai; cnt %= mod;}
void kmp() {
for(int i = 2, j = 0; i <= len; i++) {
while(j && (ch[i] != ch[j+1])) j = next[j];
ch[i] == ch[j+1] ? j++ : j=j;
next[i] = j;
num [i] = num[j]+1;
}
cnt = 1;
for(int i = 2, j = 0; i <= len; i++) {
while(j && ch[i] != ch[j+1]) j = next[j];
ch[i] == ch[j+1] ? j++ : j=j;
while((j << 1) > i) j = next[j];
add(num[j] + 1);
}
}
int main() {
scanf("%d", &n);
while( n-- ) {
memset(next, 0, sizeof(next));
memset(num , 0, sizeof(num ));
scanf("%s", ch+1);
len = strlen(ch+1);
num[1] = 1; cnt = 1;
kmp();
printf("%lld\n", cnt);
}
return 0;
}