[NOI2014][bzoj3670] 动物园 [kmp+next数组应用]

题面

传送门

思路

首先,这题最好的一个地方,在于它给出的关于$next$的讲解实在是妙极......甚至可以说我的kmp是过了这道题以后才脱胎换骨的

然后是正文:

如何求$num$数组?

这道题的输入有1e6个字符,显然需要$O\left(n\right)$左右级别的算法来解

先看到$num$的定义:不互相重叠的公共前后缀个数

这说明什么?

说明$num$不同于$next$记录的是一个最大值,它记录的是一个和值

而这个和值,是可以推出来的

考虑一个前缀$i$的$next[i]$,它长这样:

其中,$next[i]$,$next[next[i]]$,$next[next[next[i]]]$......都是这个前缀串i的公共前后缀,而且只有它们是公共前后缀

那么,我们其实只要在求$next$的过程中,顺便把这个公共前后缀的数量递推一下,就得到了一个弱化版的$num$数组:可以重叠的公共前后缀数量,我们称之为$ans$

如何去除有重叠的?

还是看上面那张图

首先$next$数组有一个性质:$next[i] < i$

也就是说,一旦有一个递归了n层的next,比原前缀i的长度的一半要小,那么这个next的递推出的答案$ans$就是i的$num$了

一个问题

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

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

减少重复递归......有没有想到什么?

没错,就是如同求$next$时一样的方法!

我们将递归用的变量$j$的值不更新,这样,求完了$i$的答案以后,$j$的位置一定在$\frac i2$的左边,也就是它已经满足要求了

这时再递归求解,总时间效率是$O\left(n\right)$的

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll MOD=1e9+7;
int n,fail[1000010],ans[1000010];ll cnt;char a[1000010];
int main(){
    int T,i,j;scanf("%d",&T);
    while(T--){
        scanf("%s",a);n=strlen(a);
        memset(fail,0,sizeof(fail));
        j=0;ans[0]=0;ans[1]=1;

        for(i=1;i<n;i++){//求解next
            while(j&&(a[i]!=a[j])) j=fail[j];
            j+=(a[i]==a[j]);fail[i+1]=j;ans[i+1]=ans[j]+1;//递推记录ans
        }

        j=0;cnt=1;
        for(i=1;i<n;i++){//求解num
            while(j&&(a[i]!=a[j])) j=fail[j];
            j+=(a[i]==a[j]);
            while((j<<1)>(i+1)) j=fail[j];
            cnt=(cnt*(ll)(ans[j]+1))%MOD;//记得+1
        }
        printf("%lld\n",cnt);
    }
}

原文地址:https://www.cnblogs.com/dedicatus545/p/8900494.html

时间: 2024-10-16 17:38:24

[NOI2014][bzoj3670] 动物园 [kmp+next数组应用]的相关文章

uoj #5. 【NOI2014】动物园 kmp

#5. [NOI2014]动物园 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://uoj.ac/problem/5 Description 近日,园长发现动物园中好吃懒做的动物越来越多了.例如企鹅,只会卖萌向游客要吃的.为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的园长决定开设算法班,让动物们学习算法. 某天,园长给动物们讲解KMP算法. 园长:“对于一个字符串S它的长度为L.我们可以在O(L)的时间内求出一个名为next

【BZOJ3670】【NOI2014】动物园 [KMP][倍增]

动物园 Time Limit: 10 Sec  Memory Limit: 512 MB[Submit][Status][Discuss] Description 近日,园长发现动物园中好吃懒做的动物越来越多了.例如企鹅,只会卖萌向游客要吃的.为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定开设算法班,让动物们学习算法. 某天,园长给动物们讲解KMP算法. 园长:“对于一个字符串S,它的长度为L.我们可以在O(L)的时间内,求出一个名为next的数组.有谁预习了next数

【NOI2014】动物园 - KMP

题目描述 近日,园长发现动物园中好吃懒做的动物越来越多了.例如企鹅,只会卖萌向游客要吃的.为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定开设算法班,让动物们学习算法. 某天,园长给动物们讲解KMP算法. 园长:“对于一个字符串S,它的长度为L.我们可以在O(L)的时间内,求出一个名为next的数组.有谁预习了next数组的含义吗?” 熊猫:“对于字符串S的前i个字符构成的子串,既是它的后缀又是它的前缀的字符串中(它本身除外),最长的长度记作next[i].” 园长:“非

BZOJ_3670_[NOI2014]_动物园(kmp)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=3670 对于一个字符串,求出num数组,其中num[i]表示前i个字符构成的字串中不相重合的相同前后缀共有多少对. 分析 在做kmp的自匹配时,顺便记录一个cnt数组,其中cnt[i]表示第i个字符需要进行几次i=fail[i]到达0,也就是如果失配时跳转到i位置,包括i位置,之前共有多少可以作为失配时跳转对象的(也就是有多少满足和后面某一后缀相同的前缀).第二遍做子匹配时,将失配指针一直跳到

UOJ #5. 【NOI2014】动物园 扩大KMP

第一次NOI称号. ... 扩展假设知道KMP如果. .. . 就是水题了. ... #5. [NOI2014]动物园 统计提交情况 描写叙述 提交 近日.园长发现动物园中好吃懒做的动物越来越多了.比如企鹅.仅仅会卖萌向游客要吃的. 为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的园长决定开设算法班.让动物们学习算法. 某天,园长给动物们解说KMP算法. 园长:"对于一个字符串S它的长度为L.我们能够在O(L)的时间内求出一个名为next的数组.有谁预习了next数组的含义吗?&

UOJ #5. 【NOI2014】动物园 扩展KMP

第一次做NOI的题.... 如果知道扩展KMP的话....就是水题了.... #5. [NOI2014]动物园 统计提交情况 描述 提交 近日,园长发现动物园中好吃懒做的动物越来越多了.例如企鹅,只会卖萌向游客要吃的.为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的园长决定开设算法班,让动物们学习算法. 某天,园长给动物们讲解KMP算法. 园长:"对于一个字符串S它的长度为L.我们可以在O(L)的时间内求出一个名为next的数组.有谁预习了next数组的含义吗?" 熊猫

Luogu P2375 动物园 kmp

P2375 动物园 题目描述 近日,园长发现动物园中好吃懒做的动物越来越多了.例如企鹅,只会卖萌向游客要吃的.为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定开设算法班,让动物们学习算法. 某天,园长给动物们讲解KMP算法. 园长:“对于一个字符串S,它的长度为L.我们可以在O(L)的时间内,求出一个名为next的数组.有谁预习了next数组的含义吗?” 熊猫:“对于字符串S的前i个字符构成的子串,既是它的后缀又是它的前缀的字符串中(它本身除外),最长的长度记作next[

HDOJ3336 Count the string 【KMP前缀数组】+【动态规划】

Count the string Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4212    Accepted Submission(s): 1962 Problem Description It is well known that AekdyCoin is good at string problems as well as n

字符串匹配KMP next数组的理解

#include<cstdio> #include<cstring> void getNext(int *Next,char* src){ int i,j; Next[0]=-1; i=0; j=-1; int N=strlen(src); while(i<N-1){ if(j==-1||src[i]==src[j]){ ++i; ++j; Next[i]=j; }else{ /* 理解难点:假设已经存在Next:假设是两个字符串在进行比较. 1. a)现在有两个字符串 sr