HDU 6153 A Secret
题意
先翻过来,后缀先变前缀(不想描述后缀啦^_^)
给出两个串,这里我们记作S串和T串
对于T串的一个前缀t串,求出t串在S串中出现了多少次,这个小t串对答案的贡献就是”次数 * t的长度”
求T串所有符合条件的前缀对答案的贡献和
解决
- 对拓展KMP没经验^_^,所以现场没写出来,惭愧
- 不管三七二十一,先翻转过来再说…
- 重点,我们求出在S串里,以下标i开始,有多长的字符串可以与T串的前缀完全匹配
- 拓展KMP的extend数组恰好可以找到这个长度
- 知道了每一个位置的匹配长度.我们知道,如果我们匹配到了一个长度为n的串.这个串的每一个前缀的长度都会对答案有一次贡献,而答案恰恰是这些一次次贡献的和.
- 我们利用等差数列求和公式,即可快速求解…
- 为方便理解,用样例来演示一下extend数组
S=abababab
T=aba
sub: 0 1 2 3 4 5 6 7
S(reversed): b a b a b a b a
extend[i] 0 3 0 3 0 3 0 1
extend[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 << endl
const 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;
}
}