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

题目链接

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

思路

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

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

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

cnt[1]=1


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

然后我们可以通过 cnt[]next[] 数组得到 num[] 数组。具体做法是,对于每个 i>=2 ( i=1 时显然 num[i]=0 ),通过沿着 next[] 指针向前走找到最大的 j , j 满足前缀 j 既是前缀 i 的前缀,也是它的后缀,且 2j<=i (即题面中所限制的前缀和后缀不重叠),那么 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;
}
    原文作者:KMP算法
    原文地址: https://blog.csdn.net/qpswwww/article/details/44937169
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞