HDU 6153 A Secret(拓展KMP)

来源:互联网 发布:成都域名服务器地址 编辑:程序博客网 时间:2024/06/06 00:45

HDU 6153 A Secret

题意

先翻过来,后缀先变前缀(不想描述后缀啦^_^)

给出两个串,这里我们记作S串和T串

对于T串的一个前缀t串,求出t串在S串中出现了多少次,这个小t串对答案的贡献就是”次数 * t的长度”

求T串所有符合条件的前缀对答案的贡献和

解决

  1. 对拓展KMP没经验^_^,所以现场没写出来,惭愧
  2. 不管三七二十一,先翻转过来再说…
  3. 重点,我们求出在S串里,以下标i开始,有多长的字符串可以与T串的前缀完全匹配
  4. 拓展KMP的extend数组恰好可以找到这个长度
  5. 知道了每一个位置的匹配长度.我们知道,如果我们匹配到了一个长度为n的串.这个串的每一个前缀的长度都会对答案有一次贡献,而答案恰恰是这些一次次贡献的和.
  6. 我们利用等差数列求和公式,即可快速求解…
  7. 为方便理解,用样例来演示一下extend数组
S=ababababT=abasub:          0 1 2 3 4 5 6 7S(reversed):  b a b a b a b aextend[i]     0 3 0 3 0 3 0 1extend[1]=3表示:以a(S[1])打头,可以有3个长度去与T串完全匹配extend[7]=1...同理

如果想看extend数组的值是怎么变化的,取消掉注释就可以看到

#include <algorithm>#include <iostream>#include <cstring>#include <cstdio>using namespace std;#define de(x) cout << #x << "=" << x << endlconst int maxn = 1e6+5;const int MOD = 1e9 + 7;int n;char S[maxn],T[maxn];int fail[maxn],extend[maxn];    //next和fail其实是一个意思,都表示失败后回跳到哪里                                //但是不知道为什么用next的时候,HDU上会报编译错误/(ㄒoㄒ)/~~//这里用的是kuangbin大神的模板//next[i]:x[i...m-1]与x[0...m-1]的最长公共前缀//extend[i]:y[i...n-1]与x[0...m-1]的最长公共前缀void pre_EKMP(char x[],int m,int fail[]){    fail[0]=m;    int j=0;    while(j+1<m && x[j]==x[j+1]) j++;    fail[1]=j;    int k=1;    for(int i=2;i<m;i++)    {        int p=fail[k]+k-1;        int L=fail[i-k];        if(i+L<p+1) fail[i]=L;        else        {            j=max(0,p-i+1);            while(i+j<m && x[i+j]==x[j]) j++;            fail[i]=j;            k=i;        }    }}void EKMP(char x[],int m,char y[],int n,int fail[],int extend[]){    pre_EKMP(x,m,fail);    int j=0;    while(j<n&&j<m&&x[j]==y[j]) j++;    extend[0]=j;    int k=0;    for(int i=1;i<n;i++)    {        int p=extend[k]+k-1;        int L=fail[i-k];        if(i+L<p+1) extend[i]=L;        else        {            j=max(0,p-i+1);            while(i+j<n&&j<m&&y[i+j]==x[j]) j++;            extend[i]=j;            k=i;        }    }}int main(){    int cases;    scanf("%d",&cases);    while(cases--)    {        scanf("%s",S);        scanf("%s",T);        int len_s=strlen(S) , len_t=strlen(T);        reverse(S,S+len_s);        reverse(T,T+len_t);        //de(S);de(T);        memset(fail,0,sizeof(fail));        memset(extend,0,sizeof(extend));        EKMP(T,len_t,S,len_s,fail,extend);        /*for(int i=0;i<len_s;i++)            printf("%d%c",extend[i],i==len_s-1?'\n':' ');*/        long long ans=0,tmp,n;        for(int i=0;i<len_s;i++)        {            if(extend[i])            {                n=extend[i]%MOD;                tmp=(n*(n+1)/2)%MOD;        //等差数列求和:n(n+1)/2                ans=(ans+tmp);                if(ans>MOD) ans-=MOD;            }        }        cout<<ans<<endl;    }}