题解 P2375 【[NOI2014]动物园】
本题的精髓在于熟练掌握kmp以及递归转递推的思想,通过递推,我们就可以让
思路
在做本题之前,我们先来想一道这样的问题:
一个字符串s,求出它的
log 数组一一对于字符串s 的前i 个字符构成的子串,既是它的后缀同时又是它的前缀的字符串的数量(并且该前缀不能是字符串本身)。
比如字符串
ababa 的log 数组就是:s[1]=0 \ ,\ s[2] = \ 0,\ s[3] = \ 1,\ s[4] = \ 1,\ s[5] = \ 2
对于这个问题,我们可以思考一下它和
比如字符串ababa的
next 数组就是next[1]=0 \ ,\ next[2] = \ 0,\ next[3] = \ 1,\ next[4] = \ 2,\ next[5] = \ 3
这个东西有什么用呢?我也不知道喵!
让我们把字符串和他的next列出来看看:
| 字符串前缀子串 | |
|---|---|
| a | |
| ab | |
| aba | a |
| abab | ab |
| ababa | aba |
在我们求"ababa"的
| ababa的相同前后缀 | |
|---|---|
| aba | +1 |
| a | +1 |
诶,这怎么和递归next数组的过程一样呢?让我们画图来了解一下。
已知一个字符串的最长相同前后缀就是它的
因为
所以我们把这个字符串的
所以,求l
log[x]=log[next[x]] + 1
PS:当
让我们回到题目:
求出一个更强大的
num 数组 一一 对于字符串S 的前i 个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠的字符串的数量。
很简单,我们直接放弃
那我们应该怎么办呢?再次模拟一下
| ababababa相同前后缀 | |
|---|---|
| abababa | +0 |
| ababa | +0 |
| aba | +1 |
| a | +1 |
显然,如果我们知道一个字符串的最长 不重叠相同前后缀 (对于
num[x]=log[EXnext[x]] + 1
PS:当
所以本题的关键在于如何求出
回忆
容易发现,从左往右扫,重叠的情况一开始一定有且只有一个字符是重叠的。当我们求解
最后上代码,很丑请见谅QAQ:
#include <cstdio>
#define ll long long
using namespace std;
const int N=1000500;
const ll mod=1000000007;
int next[N],EXnext[N],len;
ll log[N],num[N];
char s[N];
inline void getnext(){
int p=0;next[1]=0;
for(int i=2;s[i];++i){
while(p&&s[i]!=s[p+1]) p=next[p];
if(s[i]==s[p+1]) ++p;
next[i]=p;
}
}
inline void getnum(){
ll Ans=1;
ll p=0;EXnext[1]=0;
for(int i=2;s[i];++i){
while(p&&s[i]!=s[p+1]) p=EXnext[p];
if(s[i]==s[p+1]) ++p;
if(i%2&&(i+1)/2==p) p=next[p];
EXnext[i]=p;
}
for(int i=1;s[i];++i){
log[i]=log[next[i]];
if(EXnext[i]) ++log[i];
}
for(int i=1;s[i];++i){
num[i]=log[EXnext[i]];
if(EXnext[i]) ++num[i];
Ans=(Ans*(num[i]+1))%mod;
}
printf("%lld\n",Ans);
}
int main(){
int n;
scanf("%d",&n);
while(n--){
scanf("%s",s+1);
getnext();
getnum();
}
return 0;
}