1. 程式人生 > >BZOJ3670: [Noi2014]動物園

BZOJ3670: [Noi2014]動物園

參考 space ++ align urn 情況 sin algorithm ext

【傳送門:BZOJ3670】


簡要題意:

  給出一個st字符串,定義一個sum數組,sum[i]表示st[1...i]中,所有前綴和後綴不重疊的情況下,前綴和後綴相等的數量,比如說st=‘aaaaa‘,sum[5]=2,因為st[1]=st[len],st[1...2]=st[len-1...len],所以有兩個,求出(sum[1]+1)*(sum[2]+1)*(sum[3]+1)*...*(sum[n]+1),答案要%10^9+7


題解:

  原題就告訴了你KMP這個算法,就用KMP來做這道題(僅此題,別的不一定,比如說某省選的一道題上說,考整體二分套可持久化平衡樹,結果不是)

  首先像平時求kmp一樣求p數組,然後定義一個dep數組,dep[i]表示p[i]是經過多少次向前繼承得到的(有點繞口,但是很重要!要理解好),並且我們設如果p[i]=0時,dep[i]=1,比如說st=‘abcababc‘,p數組=0 0 0 1 2 1 2 3,dep數組=1 1 1 2 2 2 2 2,求出dep有什麽用呢?

  dep就是sum數組的雛形,它表示所有前綴和後綴相等的數量,有可能有重疊的前綴和後綴,也有可能就是整個子串,所以為什麽p[i]=0時,dep[i]=1

  接下來就是要得出正確的dep數組,其實怎麽來求呢,操作其實和求p數組的操作相似,不過一開始要先將j變為長度小於等於i/2,因為不能重疊,然後ans直接在每一次操作中操作


參考代碼:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
char st[1100000];
LL dep[1100000];
LL p[1100000];
LL sum[1100000];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf(
"%s",st+1); int len=strlen(st+1); LL ans=1; memset(p,0,sizeof(p)); memset(sum,0,sizeof(sum)); memset(dep,0,sizeof(dep)); dep[1]=1; for(int i=2;i<=len;i++) { int j=p[i-1]; while(st[j+1]!=st[i]&&j!=0) j=p[j]; if(st[j+1]==st[i]) j++; p[i]=j; dep[i]=dep[j]+1; } int j=0; for(int i=2;i<=len;i++) { while((j+1)*2>i) j=p[j]; while(j!=0&&st[i]!=st[j+1]) j=p[j]; if(st[i]==st[j+1]) j++; ans=(ans*(dep[j]+1))%1000000007; } printf("%lld\n",ans); } return 0; }

BZOJ3670: [Noi2014]動物園