您的位置:首页 > 其它

HDU 6153 A Secret【KMP||扩展KMP】

2017-09-15 15:36 519 查看

A Secret

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit:256000/256000 K (Java/Others)

Total Submission(s): 2513    Accepted Submission(s): 926


Problem Description

Today is the birthday of SF,so VS gives two strings S1,S2 to SF as a present,whichhave a big secret.SF is interested in this secret and ask VS how to getit.There are the things that VS tell:

Suffix(S2,i) = S2[i...len].Ni is the times that Suffix(S2,i) occursin S1 and Li is the length of Suffix(S2,i).Then the secret is the sum of theproduct of Ni and Li.

Now SF wants you to help him find the secret.The answer may be verylarge, so the answer should mod 1000000007.

 
 
Input

Input contains multiple cases.

The first line contains an integer T,the number of cases.Thenfollowing T cases.

Each test case contains two lines.The first line contains a stringS1.The second line contains a string S2.

1<=T<=10.1<=|S1|,|S2|<=1e6.S1 and S2 only consist oflowercase ,uppercase letter.

 
 
Output

For each test case,output a single line containing a integer,the answer of testcase.

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

 

case2:

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.

【题意】

给出两个串S和T,求串T的后缀在串S中出现的次数*后缀长度乘积的和。

【思路】

设母串为S,子串为T。

方法一:拓展kmp算法

我们先把两个串都反一下,那么就变成串T的前缀在串S中出现的次数*后缀长度乘积的和。

我们知道拓展kmp可以算出extend数组,而extend[i]表示S[i,lens-1]与T的最长公共前缀。

那么显然每个extend[i]对答案的贡献为(extend[i]*(extend[i]+1))/2。

举个例子:

S串: abbabbab

T串: abbc

extend[3]=3,那么长度为1,2,3的“a”,"ab","abb"各匹配一次,贡献为(1+2+3),即(1+2+...+extend[i]),即(extend[i]*(extend[i]+1))/2。

#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn = 1000005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;

int nex[maxn];
int extend[maxn];

void pre_exkmp(char *b)
{
int len=strlen(b);
nex[0]=len;
int j=0;
while(j+1<len&&b[j]==b[j+1]) j++;
nex[1]=j;
int k=1;
for(int i=2;i<len;i++)
{
int p=nex[k]+k-1;
int L=nex[i-k];
if(i+L<p+1) nex[i]=L;
else
{
j=max(0,p-i+1);
while(i+j<len&&b[i+j]==b[j]) j++;
nex[i]=j;
k=i;
}
}
}

void exkmp(char *a,char *b)
{
pre_exkmp(b);
int j=0;
int lena=strlen(a);
int lenb=strlen(b);
while(j<lena&&j<lenb&&a[j]==b[j]) j++;
extend[0]=j;
int k=0;
for(int i=1;i<lena;i++)
{
int p=extend[k]+k-1;
int L=nex[i-k];
if(i+L<p+1) extend[i]=L;
else
{
j=max(0,p-i+1);
while(i+j<lena&&j<lenb&&a[i+j]==b[j]) j++;
extend[i]=j;
k=i;
}
}
}

ll add(ll x)
{
ll ans=x%mod*((x+1)%mod)%mod*500000004%mod; //有除法,用到逆元
return ans;
}

char a[maxn];
char b[maxn];

int main()
{
rush()
{
scanf("%s%s",a,b);
int lena=strlen(a);
int lenb=strlen(b);
reverse(a,a+lena);
reverse(b,b+lenb);
exkmp(a,b);
ll ans=0;
for(int i=0;i<lena;i++)
{
if(extend[i])
{
ans=(ans+add(extend[i]))%mod;
}
}
printf("%I64d\n",ans);
}
return 0;
}


方法二: kmp算法

可以先参考一下这道题的做法: 1277 字符串中的最大值

同样先把S串和T串反一下。

在这道题里,我们先用KMP算法求出T长度为i的前缀在S中出现的次数。

但由于kmp算法中由于nex数组的存在,回溯时跳过了一部分位置,所以我们需要把它们加回去。

根据nex数组的定义,nex[i]表示既是S[0,i-1]的前缀又是其后缀的串的最大长度。

那么举个例子,对于串: “abcdab”

nex[6]=2,由于这段长度被跳过了,那么长度为2的需要加上这一部分。即:

dp[nex[i]]+=dp[i]

建议自己模拟一下。

然后累加一下就可以了。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn = 1000005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f;
const double eps = 1e-9;

int nex[maxn];
ll dp[maxn];
char s[maxn];
char t[maxn];

void kmp_pre(char *b)
{
int i=0,j=-1;
int len=strlen(b);
nex[0]=-1;
while(i<len)
{
while(j!=-1&&b[i]!=b[j]) j=nex[j];
if(b[++i]==b[+
4000
+j]) nex[i]=j;
else nex[i]=j;
}
}

void kmp(char *a,char *b)
{
int i=0,j=0;
kmp_pre(b);
int lena=strlen(a),lenb=strlen(b);
while(i<lena)
{
while(j!=-1&&a[i]!=b[j]) j=nex[j];
i++,j++;
dp[j]++; //长度为j的前缀匹配数目加1
if(j>=lenb)
{
j=nex[j];
}
}
}

int main()
{
rush()
{
mst(dp,0);
scanf("%s%s",s,t);
int lens=strlen(s);
int lent=strlen(t);
reverse(s,s+lens);
reverse(t,t+lent);
kmp(s,t);
ll ans=0;
for(int i=lent;i>=1;i--)
{
dp[nex[i]]+=dp[i];
ans=(ans+dp[i]*i)%mod;
}
printf("%I64d\n",ans);
}
return 0;
}


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息