版权声明:转载请注明文章出处 https://blog.csdn.net/zhj_fly/article/details/79261079
题意:给两个字符串S1和S2,求S2长度为i的后缀在S1中出现的次数为num[i],求所有的i*num[i]的和(模1e9+7)。
解析:此题可以利用KMP中next数组来做,求next数组不变,在KMP两个串匹配中稍作修改即可。
首先将两个串反转一下,这样就是求S2的前缀在S1中出现的次数了。
在KMP匹配中,用一个数组num来记录长度i的前缀的次数。每次匹配到长度为i的前缀时,num[i]++。
但是这样会漏掉一些计数,比如S1:abababa, S2:aba,当S2中匹配到后面的a时,即i和j都是3时,num数组只会给num[3]++,但此时S1的第二个a和S2的第一个a也匹配,但是num[1]并没有加1。
解决方法:再反响遍历一遍num数组,利用next数组,num[next[i]] += next[i],即可将遗漏的加上。
代码:
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
typedef long long ll;
const int N = 1000005;
const ll mod = 1e9+7;
char str1[N], str2[N];
int Next[N];
int len1, len2;
ll num[N];
void GetNext(){
int i = 0, j = -1;
Next[0] = -1;
while(i < len2){
if(j == -1 || str2[i] == str2[j]){
i++, j++;
Next[i] = j;
}
else
j = Next[j];
}
}
void KMP(){
GetNext();
int i = 0, j = 0;
while(i < len1){
if(j == -1 || str1[i] == str2[j]){
i++, j++;
num[j]++;
}
else
j = Next[j];
}
}
int main(){
std::ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--){
cin>>str1>>str2;
len1 = strlen(str1);
len2 = strlen(str2);
reverse(str1, str1+len1);
reverse(str2, str2+len2);
str2[len2++] = '#';//当S2到最后一个字符后,利用next数组自动返回到前面某个位置
memset(num, 0, sizeof(num));
KMP();
ll ans = 0;
for(int i = len2-1; i > 0; i--){
ans = (ans + num[i] * i) % mod;
num[Next[i]] += num[i];
}
cout<<ans<<endl;
}
return 0;
}