HDU 6153 A Secret(KMP)

来源:互联网 发布:centos安装图形界面 编辑:程序博客网 时间:2024/06/10 19:18

A Secret


Problem Description
Today is the birthday of SF,so VS gives two strings S1,S2 to SF as a present,which have a big secret.SF is interested in this secret and ask VS how to get it.There are the things that VS tell:
  Suffix(S2,i) = S2[i...len].Ni is the times that Suffix(S2,i) occurs in S1 and Li is the length of Suffix(S2,i).Then the secret is the sum of the product of Ni and Li.
  Now SF wants you to help him find the secret.The answer may be very large, so the answer should mod 1000000007.
 

Input
Input contains multiple cases.
  The first line contains an integer T,the number of cases.Then following T cases.
  Each test case contains two lines.The first line contains a string S1.The second line contains a string S2.
  1<=T<=10.1<=|S1|,|S2|<=1e6.S1 and S2 only consist of lowercase ,uppercase letter.
 

Output
For each test case,output a single line containing a integer,the answer of test case.
  The answer may be very large, so the answer should mod 1e9+7.
 

Sample Input
2aaaaaaaabababababa
 

Sample Output
1319
Hint
case 2: Suffix(S2,1) = "aba",Suffix(S2,2) = "ba",Suffix(S2,3) = "a".N1 = 3,N2 = 3,N3 = 4.L1 = 3,L2 = 2,L3 = 1.ans = (3*3+3*2+4*1)%1000000007.
 

Source
2017中国大学生程序设计竞赛 - 网络选拔赛

题意:给出一个串S和一个串T,求T中每个长度为li(l<=1i<=|T|)的后缀在S中出现的次数ni,然后ans=Σ(li*ni)。

分析:网上有很多博客都用了扩展KMP的写法,但是鶸还不会扩展KMP,队友说了可以直接用KMP的做法来做。找了一些博客,自己想了一下,对于直接KMP有一点小思路。首先简单说明一下我对KMP的理解,对于主串S和模式串T,我们先对模式串T求next值,这里next[i]=k的意思是,在T串中,长度为i的串存在长度为k的最长相同真前后缀,那么在将S和T进行KMP的时候,实质就是在S中寻找前缀为T的字串。下面回到题目。题目要求后缀,那么我们将两个串S和T反转一下,就是转化为求前缀的问题,即求反转后,T的前缀在S中出现的次数问题。这里进行KMP的时候,假设num[j]是T中长度为j的前缀出现的次数,那么这里的num是否就是我们最后要求前缀出现的次数?答案是错的。我们来看一个例子:S:abab,T:abab,这里我们对num计数,会发现前缀数目出现的次数依次是1,1,1,1,而正确结果应该是a(2),ab(2),aba(1),abab(1),
这是因为在KMP中,我们的计数过程是这样的a,ab,aba,abab。这样我们就丢失了某些匹配,但是我们发现,在aba的时候,出现了前缀a;在abab的时候,出现了前缀ab,也就是说,在匹配长度是3的时候,KMP中出现了长度为1的前缀;在匹配长度是4的时候,出现了长度为2的前缀,那么这不正好是next数组的作用吗?也就是说,我们在对当前匹配长度j计数时,会有num[j]++和num[next[j]]++;令t=next[j]然后num[t]++出现,又会有num[next[t]]++,这里会有回溯到-1结束,但是很明显,这样会超时。。T_T然后我们来思考一下,j出现k次,那么num[next[j]]++也会出现k次,那么我们可以直接从最终结果出发,对于长度j,很明显num[j]会对num[next[j]]有num[j]次的贡献,这里满足j>next[j],然后就是从尾部反过来直接计数,回溯的过程相当于最后的叠加。

AC代码:
#include<bits/stdc++.h>using namespace std;char str[1000005];char temp[1000005];int len1,len2;int next_[1000005];long long num[1000005];const int mod=1e9+7;void Reserve(){    len1=strlen(str);    len2=strlen(temp);    for(int i=0;i<len1/2;i++){        swap(str[i],str[len1-1-i]);    }    for(int i=0;i<len2/2;i++){        swap(temp[i],temp[len2-1-i]);    }}void get_next(){    int i=0;    int j=-1;    next_[0]=-1;    while(i<len2){        if(j==-1||temp[i]==temp[j]){            i++;            j++;            next_[i]=j;        }        else{            j=next_[j];        }    }}void KMP(){    int i=0;    int j=0;    while(i<len1){        if(j==-1||str[i]==temp[j]){            i++;            j++;        }        else{            j=next_[j];        }        num[j]++;        if(j==len2){            j=next_[j];        }    }}int main(){    int N;    scanf("%d",&N);    while(N--){        memset(num,0,sizeof(num));        scanf("%s%s",str,temp);        Reserve();        get_next();        KMP();        long long ans=0;        for(int i=len2;i>0;i--){            num[next_[i]]+=num[i];            ans+=(num[i]*i);            ans%=mod;        }        printf("%lld\n",ans);    }    return 0;}


原创粉丝点击