BZOJ 3670 [Noi2014] 动物园 KMP

题目大意:给出一个长为n的字符串(n<=1e6),求num数组的累乘和模 1,000,000,007,其中num(i)={长度为i的前缀中字符串S’ 的数量,其中S ‘既是该前缀的前缀也是该前缀的后缀,且该后缀与该前缀不重叠}

题目背景已经铺垫了,该题可以用KMP解决。

在计算nxt数组时,我们可以顺便计算出cnt数组(跳几次nxt到0),表示有多少前缀可以匹配到当前前缀包含的后缀,举个例子,niconiconi这个字符串中,nxt(10)=6,nxt(6)=2,nxt(2)=0,那么cnt(10)=3.表示长度分别为10(niconiconi)/6(niconi)/2(ni)的3个前缀可以在长度为10的前缀中找到一个后缀与其匹配。

cnt数组并不是num数组,因为会有不满足条件的字符串计算在内,所以再次进行匹配,将不满足条件的去掉,也就是要满足fix(已经匹配的前缀长度)*2 < i(当前前缀的长度),此时统计答案即可。

#include <cstdio>
#include <cstring>
#define N 1000005
#define MOD 1000000007
using namespace std;
typedef long long LL;
char s[N];
int nxt[N],cnt[N];
LL KMP() {
    memset(nxt,0,sizeof nxt);
    LL ans=1; cnt[1]=1;
    int len=strlen(s+1),fix=0;
    for(int i=2;i<=len;i++) {
        while(s[i]!=s[fix+1] && fix) fix=nxt[fix];
        if(s[i]==s[fix+1]) fix++;
        nxt[i]=fix;
        cnt[i]=cnt[fix]+1;
    }
    fix=0;
    for(int i=2;i<=len;i++) {
        while(s[i]!=s[fix+1] && fix) fix=nxt[fix];
        if(s[i]==s[fix+1]) fix++;
        while(fix*2>i) fix=nxt[fix];
        ans*=cnt[fix]+1;
        ans%=MOD;
    }
    return ans;
}
int main() {
    int T;
    scanf("%d",&T);
    while(T--) scanf("%s",s+1) , printf("%lld\n",KMP());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值