【NOI2014T4】动物园-变形KMP

测试地址:动物园

做法:这题目很迷,一开始以为想出一种不用预处理的求num的方法,事实证明是错的......

为了方便,设prefix(s,i)为字符串s的前i个字符组成的前缀,suffix(s,i)为字符串s的后i个字符组成的后缀。

实际上我们要先求一个“假”的num数组,我们把它称为cnt数组,cnt[i]表示prefix(s,i)中既是其前缀又是其后缀的字符串个数(包括自身),于是我们就先预处理出next数组和cnt数组,然后再做一遍类似KMP的过程,这时候再注意重叠的问题:设pre为当前最大的满足prefix(prefix(s,i),pre)=suffix(prefix(s,i),pre)且pre≠i的数,如果pre*2>i,表示有重叠,则按照KMP的方法修正pre直到满足条件为止。于是我们可以知道,prefix(prefix(s,i),pre)=suffix(prefix(s,i),pre)且不重叠,且此时pre是满足条件的最大数,这时就可以得出num[i]=cnt[pre],累加答案即可。

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define p 1000000007
using namespace std;
char s[1000010];
int n,len;
long long cnt[1000010],next[1000010],ans;

void kmp()
{
  int pre=0;
  next[1]=cnt[0]=0;ans=1;
  cnt[1]=1;
  for(int i=2;i<=len;i++)
  {
	while(pre&&s[pre+1]!=s[i]) pre=next[pre];
	if (s[pre+1]==s[i]) pre++;
	next[i]=pre;
	cnt[i]=cnt[pre]+1;
  }
  pre=0;ans=1;
  for(int i=2;i<=len;i++)
  {
    while(pre&&s[pre+1]!=s[i]) pre=next[pre];
	if (s[pre+1]==s[i]) pre++;
	while((pre<<1)>i) pre=next[pre];
	ans*=cnt[pre]+1;
	ans%=p;
  }
  printf("%lld
",ans);
}

int main()
{
  scanf("%d",&n);
  s[0]='_';
  while(n--)
  {
    scanf("%s",s+1);
	len=strlen(s)-1;
	kmp();
  }
  
  return 0;
}


原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793827.html