[KMP]洛谷 P2375 [NOI2014] 动物园 题解

一开始看成长度了,那这题就超级水了。

你可以知道的一个事情,就是每对不重合前后缀都是包含在最长不重合前后缀里的。也就是说每个前缀的不重合前后缀的数量是

他从当前位置往前跳的次数。

我们先想一个朴素的方法,就是每个位置跳一下,然后暴力判是否重合。

#include <bits/stdc++.h>
#define int long long
const int N = 1e6 + 7;
const int mod = 1e9 + 7;
int m , n , pi[N] , num[N];
using namespace std;
string s;
signed main() {
	ios :: sync_with_stdio(0) , cin.tie(0) , cout.tie(0);
	cin >> m;
	while(m--) {
		cin >> s; n = s.size();
		s = '&' + s;
		for(register int i = 1; i <= n; ++i) {
			pi[i] = num[i] = 0;
		}
		for(register int i = 1 , j = 0; i <= n; ++i) {
			while(s[i + 1] != s[j + 1] && j) {
				j = pi[j];
			}
			if(s[i + 1] == s[j + 1]) {
				++j;
			}
			pi[i + 1] = j; // 正常kmp
		}
		
		for(register int i = 1 , j; i <= n; ++i) {
			j = i;
			while(j) {
				if(j * 2 <= i) {
					++num[i]; //暴力跳算不重合公共前后缀个数
				}
				j = pi[j];
			}
		}
		int sum = 1;
		for(register int i = 1; i <= n; ++i) {
			sum = sum * (num[i] + 1) % mod;
		}
		cout << sum << '\n';
	}
	return 0;
}

这是一份没有什么思维的代码,能拿到一半的分数。

思考为甚么会TLE掉,原因就是复杂度有问题。这个代码的第二部分复杂度是 \(O(n ^ 2)\) 的。

这个时候我们一拍脑袋发现,其实如果没有那个不重合的条件,公共前后缀的数量是可以递推的啊qwq

pi[i + 1] = j ; num[i + 1] = num[j] + 1; //是不是还挺显然的,就是从上一个公共前后缀转移

这样的话,我们只需要在 kmp 的过程里判一下是否重合,然后直接乘就行!

其实也就相当于加速了暴力。由于我们可以把这个过程嵌到 kmp 里,所以复杂度就变成了 \(O(n)\)

#include <bits/stdc++.h>
#define int long long
const int N = 1e6 + 7;
const int mod = 1e9 + 7;
int sum , m , n , pi[N] , num[N];
using namespace std;
string s;
signed main() {
	ios :: sync_with_stdio(0) , cin.tie(0) , cout.tie(0);
	cin >> m;
	while(m--) {
		cin >> s; n = s.size();
		s = '&' + s;
		for(register int i = 1; i <= n; ++i) {
			pi[i] = num[i] = 0;
		}
		num[1] = sum = 1;
		for(register int i = 1 , j = 0; i <= n; ++i) {
			while(s[i + 1] != s[j + 1] && j) {
				j = pi[j];
			}
			if(s[i + 1] == s[j + 1]) {
				++j;
			}
			pi[i + 1] = j , num[i + 1] = num[j] + 1;
		}
		
		for(register int i = 1 , j = 0; i <= n; ++i) {
			while(s[i + 1] != s[j + 1] && j) {
				j = pi[j];
			}
			if(s[i + 1] == s[j + 1]) {
				++j;
			}
			while(j * 2 > i + 1) { //我的习惯是 i 从1开始然后后面都加1,所以kmp这里条件可能跟我不同
				j = pi[j]; 
			}
			sum = sum * (num[j] + 1) % mod;
		}
		cout << sum << '\n';
	}
	return 0;
}

然后就没了。

posted @ 2025-04-14 22:28  「癔症」  阅读(53)  评论(0)    收藏  举报