【BZOJ3670】【codevs3319】动物园,KMP+时间优化

来源:互联网 发布:单文件编程改多文件 编辑:程序博客网 时间:2024/04/29 17:29

传送门1
传送门2
写在前面:难得题面和题解是一致的算法
思路:原始想法是先KMP建next数组,同时记录1-len每个长度的“公共前缀后缀”的总和sum[i],很容易想到sum[i]=1+sum[tmp],tmp就是长度为i的字符串中是前缀同时是后缀的字符串的长度(就是那个用next[next[…求的东西),之后判断长度1-len的next[i],如果next<i/2,num[i]=sum[next[i]],不然就令next[i]=next[next[i]],继续判断,直到符合条件为止。
这种最简单粗暴的方式可以过50分,但我们发现如果原串是一个循环字符串而且循环节特别小,例如“aaaaaaaaaaa…”,那么每次求num时,next[i]是一个字符一个字符往前蹦达的,那也就是说最坏情况下时间复杂度会达到O(len2),爆炸。(顺便说一下,蒟蒻就是被卡在这里好久,想到这个问题就头大,搞的忘记了next数组的性质,直接转移到next[i/2]上去了……竟然还能过样例)无法,蒟蒻选择看题解,发现题解的做法是把求next,sum放在一起,求答案(num)放在一起,开了两个循环,后来想了一下,求next,sum时tmp是从next[i]开始逐渐往后跳,时间复杂度均摊下来是O(len),而求答案时完全可以类似于从前往后走,每次求num时前进最多一格,后退不确定,但总的复杂度是一定的且可以接受的。
注意:答案用long long保存,sum[1]=1(它自身就是一个哦)

#include<bits/stdc++.h>#define mod 1000000007using namespace std;int tmp,T,len;int sum[1000010],next[1000010];long long ans;char s[1000010];main(){    sum[1]=1;    scanf("%d",&T);    while (T--)    {        scanf("%s",s);        ans=1;tmp=0;        for (int i=1;s[i];i++)        {            tmp=next[i];            while (tmp&&s[tmp]!=s[i]) tmp=next[tmp];            tmp+=(s[i]==s[tmp]);            next[i+1]=tmp;            sum[i+1]=sum[tmp]+1;        }        for (int i=1;s[i];i++)        {            while (tmp&&s[tmp]!=s[i]) tmp=next[tmp];            tmp+=(s[i]==s[tmp]);            while (tmp>((i+1)>>1)) tmp=next[tmp];            ans=ans*(sum[tmp]+1)%mod;        }        printf("%lld\n",ans);    }}
0 0
原创粉丝点击