[BZOJ3670][NOI2014]动物园(KMP)

11 篇文章 0 订阅

题目:

我是超链接

题解:

首先他给了我们关于next数组的提示,这挺好的。
先看到 num 的定义:不互相重叠的公共前后缀个数
说明 num不同于 next 记录的是一个最大值,它记录的是一个和值

那么我们先用kmp数组用递推求出ans[i]表示前缀后缀可以重叠的最长公共部分长度,这个可以用j蹦完之后直接ans[i]+1,加的1就是可以有个新的整体咯。

怎么去除重叠的呢?一旦有一个递归了n层的next,比原前缀i的长度的一半要小,那么这个next的递推出的答案 ans 就是i的num了。这个可以感性理解

假如我们拿到的串是1e6个’a’,那么上面那个算法就会被卡成 O(n2) O ( n 2 ) ,道理的话大家可以想一想(每一次递归都只会把next[i]变小1)

那么我们需要做一个优化,来解决这个问题,而解决问题的核心就是:减少重复递归

就是如同求 next 时一样的方法,我们不每次更新递归用的变量 j ,这样,求完了 i 的答案以后, j 的位置一定在 i2 i 2 的左边,也就是它已经满足要求了。每次j最多往右移动一位,即j++,这样保证他一直在左边。

这时再递归求解,总时间效率是 O(n) O ( n )

代码:

#include <cstdio>
#include <cstring>
#define LL long long 
using namespace std;
const int mod=1e9+7;
const int N=1000005;
int l,t[N],ans[N];char st[N];
void build()
{
    t[0]=-1;
    for (int i=0;i<l;i++)
    {
        int j=t[i];
        while (j!=-1 && st[j]!=st[i]) j=t[j];
        t[i+1]=++j; ans[i+1]=ans[j]+1;
    }
}
int main()
{
    int T;scanf("%d",&T);
    while (T--)
    {
        scanf("%s",st);
        l=strlen(st);ans[1]=1;
        build();
        LL cnt=1;int j=0;
        for (int i=1;i<l;i++)
        {
            while (j!=-1 && st[j]!=st[i]) j=t[j];
            j++;
            while (j*2>i+1) j=t[j];
            cnt=cnt*(LL)(ans[j]+1)%mod;
        }   
        printf("%lld\n",cnt);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值