[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;
}
然后就没了。