HDU 6153 A Secret(KMP变形)

版权声明:转载请注明文章出处 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;
}


猜你喜欢

转载自blog.csdn.net/zhj_fly/article/details/79261079