【KMP+优化】HDU 6153/CCPC 1003 A Secret

来源:互联网 发布:qq聊天记录数据恢复 编辑:程序博客网 时间:2024/06/05 02:27

看了网上的各种解题报告,觉得自己这个写法还算是比较简洁高效,mark一下。

题目:
HDU 6153
给a、b两个字符串,求b串的每个后缀在a中出现的次数,求次数乘后缀长度的乘积和。

分析:
这些天一直在看后缀数组,于是一看到就用后缀数组写了一发,后来发现就是普通KMP,比赛的时候还是需要冷静一点。

  1. 首先将a、b两个字符串反向,这样后缀自然得变成了前缀,求next数组才有意义。
  2. 然后将a连在b串的后面,为了在a末b首处不出现连接,结头处加一个未出现的字符,比如’$’。
  3. 最后从a的最后一个字符枚举到第一个字符,每次递归求next[i]、next[next[i]],即长度为next[i]的前缀出现次数+1,统计ans即可。

但是!

这样做TLE了。
因为当出现a=aaaaaaaaaaaaaaa,b=aaaa等类似的情况时,第三步中递归求next数组会沦为O(n),总的时间复杂度为O(n^2),GG……

所以!
我们要将第三步优化成线性的。
首先统计next[i](i为a串所在下标)数限次数p[next[i]]
我们发现next[i]的递归影响,如果将next[i]的值从大到小做的话,可以将影响暂时累计给next[next[i]],next[next[i]]会将next[i]的影响传递给next[next[next[i]]]……这样每个next[i]只需将自己的个数累加给next[i],时间为O(1),总的复杂度变成O(n),完美解决!

运行时间:468ms

#include <iostream>#include <cstdio>#include <cstring>using namespace std;const long long MOD=1e9+7;int la,lb,t;const int MAXN=2000010;char a[1000007],b[1000007],s[MAXN];int len,ne[MAXN],p[MAXN];inline void getne (){    int i,j;    ne[1]=0;    for (j=0,i=2;i<=len;i++){//j为模式串的开头位置        while (j>0&&s[i]!=s[j+1])            j=ne[j];        if (s[i]==s[j+1])            j++;        ne[i]=j;    }}inline void work (){    int i,j;    long long tmp,ans=0;    for (i=len;i>lb+1;i--){        j=i;        p[ne[j]]++;    }    for (i=la+1;i>0;i--){        if (p[i]){            if (ne[i])                p[ne[i]]+=p[i];        }    }    for (i=la+1;i>0;i--){        ans=(ans+(long long)p[i]*(long long)i%MOD)%MOD;    }    cout<<ans<<endl;}int main (){    scanf ("%d",&t);    while (t--){        scanf ("%s\n%s",a,b);        la=strlen (a);        lb=strlen (b);        memset (p,0,sizeof (p));        len=0;        for (int i=lb-1;i>=0;i--){               s[++len]=b[i];        }        s[++len]='$';        for (int i=la-1;i>=0;i--){               s[++len]=a[i];        }        getne ();        work ();    }    return 0;}

大佬们都是十几分钟就过了,自己还是不够熟练啊,还是要敲起来!