[洛谷P2375][NOI2014]动物园

题目大意:求出一个字符串$S$每一位的$num[i]$,最多$5$组询问,$|S|\leqslant10^6$

$num[i]$定义为$S_{1,i}$中长度不超过$\lfloor\dfrac i2\rfloor$的$border$的个数

题解:类似$KMP$。发现一个字符串$border$的$border$一定是原串的$border$,并且若每次是最长$border$的话,所有的$border$均会被枚举到。令$res[i]$表示$S_{1,i}$的$border$个数$+1$(即包括$S_{1,i}$),则若$S_{1,i}$为$S_{1,j}$的一个$border$且$i$为所有满足$i\leqslant\lfloor\dfrac j2\rfloor$中最大的,则$num[j]=res[i]$。$res$数组可以在求$nxt$数组时顺带求出。现在就是要对于$j$快速求出$i$。可以通过类似$KMP$的方法,求出了$j$对应的$i$,在就$j+1$时,就直接对$i$进行修改,若$i>\lfloor\dfrac j2\rfloor$,重复$i=nxt[i]$直到满足条件。这样复杂度和$KMP$一样是$O(n)$的

卡点:

C++ Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
const int maxn = 1e6 + 10, mod = 1e9 + 7;

int Tim, n, ans;
char s[maxn];
int nxt[maxn], res[maxn];
int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	std::cin >> Tim;
	while (Tim --> 0) {
		std::cin >> s + 1;
		n = strlen(s + 1), ans = 1;
		res[1] = 1;
		for (int i = 2, j = 0; i <= n; ++i) {
			while (j && s[i] != s[j + 1]) j = nxt[j];
			nxt[i] = j += s[i] == s[j + 1], res[i] = res[j] + 1;
		}
		for (int i = 2, j = 0; i <= n; ++i) {
			while (j && s[i] != s[j + 1]) j = nxt[j];
			j += s[i] == s[j + 1];
			while (j > i / 2) j = nxt[j];
			ans = static_cast<long long> (res[j] + 1) * ans % mod;
		}
		std::cout << ans << '\n';
	}
	return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/Memory-of-winter/p/11305425.html