[kmp] hdu6153 A Secret

来源:互联网 发布:广州公交线路查询软件 编辑:程序博客网 时间:2024/06/04 23:34

@(ACM题目)[字符串, kmp]

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

2
aaaaa
aa
abababab
aba

Sample Output

13
19

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.

题目分析

题目给定模式串P和文本串T,求ΣLiti,其中LiP的后缀i的长度,ti为它在T中出现的次数。

将两个字符串反转,转化为前缀相关问题。对于T中每个前缀Ti,统计其后缀对答案的贡献resi,则Σresi即为答案。

现计算resi,在“T的前缀Ti”的所有后缀中,找到一个最长的后缀,使它是P的前缀,记这个P的前缀为Pj,显然Pj可以用kmp求得。

现在我们拿到了这个Pj,怎样计算resi呢?注意到这个Pj是模式串P的前缀,完全可以在kmp构建fail指针的过程中顺便计算出它对答案的贡献。

sumj+1Pj对答案的贡献,则只要将Pj中所有“前缀等于后缀”的子串的长度累和即可。

举例说明,对于字符串aabaax来说,考察x处的sum5=lengthaabaa+lengthaa+lengtha=5+2+1=8

为什么计算“前缀等于后缀”的子串呢?以上面的例子说明,若在Ti到后缀中匹配到aabaa这个字符串,如前所说,aabaa也是P的前缀,那么由aabaa的后缀aabaaaaaaabaa的前缀,就可知它们也是P的前缀了。

如果觉得此法不容易想,我们可以把它当做模板题来做,详见这里。

代码

#include<bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 1e6 + 5;const LL M = 1e9 + 7;char a[maxn]{0}, b[maxn]{0};LL sum[maxn];int fail[maxn];void getFail(char* P){    int m = strlen(P);    fail[0] = fail[1] = 0;    for(int i = 0; i <= m; ++i) sum[i] = i;    for(int i = 1; i < m; i++)    {        int j = fail[i];        while(j && P[i] != P[j]) j = fail[j];        if(P[i] == P[j])        {            fail[i + 1] = j + 1;            sum[i + 1] = (sum[i + 1] + sum[j + 1]) % M;        }        else fail[i + 1] = 0;    }}LL finda(char* T, char* P){    LL res = 0;    int n = strlen(T), m = strlen(P);    getFail(P);    int j = 0;    for(int i = 0; i < n; i++)    {        while(j && T[i] != P[j]) j = fail[j];        if(T[i] == P[j]) ++ j;        res = (res + sum[j]) % M;    }    return res;}int main(){    int T;    cin >> T;    while(T--)    {        scanf("%s%s", a, b);        reverse(a, a + strlen(a));        reverse(b, b + strlen(b));        cout << finda(a, b) << endl;    }    return 0;}
原创粉丝点击