您的位置:首页 > 其它

[省选前题目整理][BZOJ 3670][NOI 2014]动物园(KMP)

2015-04-08 10:18 344 查看

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=3670

思路

为了方便叙述,下面所说的前缀ii就是字符串中的区间[1,i][1,i]对应的子串

根据KMP定义,next[i]=jnext[i]=j表示前缀j前缀j满足既是前缀ii的前缀,又是前缀ii的后缀,且jj是最大的。(其实题目里对此也有说明)

然后我们可以根据next[]next[]数组构造一个cnt[]cnt[]数组,cnt[i]=cnt[i]=满足既是前缀ii的前缀,又是前缀ii的后缀的子串个数,这个数组可以通过递推在O(n)O(n)时间内得出:

cnt[1]=1cnt[1]=1

cnt[i]=cnt[next[i]]+1cnt[i]=cnt[next[i]]+1

然后我们可以通过cnt[]和next[]cnt[]和next[]数组得到num[]num[]数组。具体做法是,对于每个i>=2i>=2(i=1i=1时显然num[i]=0num[i]=0),通过沿着next[]next[]指针向前走找到最大的jj,jj满足前缀jj既是前缀ii的前缀,也是它的后缀,且2j<=i2j<=i(即题面中所限制的前缀和后缀不重叠),那么num[i]=cnt[j]num[i]=cnt[j]。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 1000100
#define MOD 1000000007

using namespace std;

typedef long long int LL;

LL ans=0;
char s[MAXN];
int next[MAXN],cnt[MAXN]; //cnt[i]=既是1~i的后缀又是它的前缀的字符串个数

void getnext(char str[],int len)
{
int k=0;
next[1]=0;
cnt[1]=1; //!!!!!!
for(int i=2;i<=len;i++)
{
while(k>0&&str[k+1]!=str[i]) k=next[k];
if(str[k+1]==str[i]) k++;
next[i]=k;
cnt[i]=cnt[k]+1;
}
}

LL work(char str[],int len)
{
int k=0;
ans=1;
for(int i=2;i<=len;i++) //用next[]和cnt[]数组求出num[i]
{
while(k&&str[k+1]!=str[i]) k=next[k];
if(str[k+1]==str[i]) k++;
while((k*2)>i) k=next[k];
ans=(ans*(cnt[k]+1))%MOD;
}
return ans;
}

int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",s+1);
int len=strlen(s+1);
getnext(s,len);
printf("%lld\n",work(s,len));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: