CodeForces 526D Om Nom and Necklace

洛谷传送门

CF 传送门

思路

题意相当于将 \(S\) 表示成 \(A^kB\)\(A^x = A^{x-1}A\)\(A^0\) 为空串),其中 \(B\)\(A\) 的前缀。

考虑枚举 \(|A^k|\),设 \(|A^k| = len\ (k\ |\ len)\),在 \([1,len]\) 中寻找长度为 \(\dfrac{len}{k}\) 的循环节。看到循环节就想到 KMP,预处理出 \(fail_i\),那么 \([1,len]\) 的最短的循环节长度为 \(len - fail_{len}\)。当 \(len - fail_{len} \nmid len\) 或者 \(len < 2 \cdot (len - fail_{len})\)(即循环次数 \(< 2\);注意当 \(m=1\) 时这个条件可以不满足),则 \([1,len]\) 不存在循环节。现在已经知道了最短的循环节长度为 \(len - fail_{len}\),若 \([1,len]\) 存在长度为 \(\dfrac{len}{k}\) 的循环节,那么必须满足 \((len - fail_{len})\ |\ \dfrac{len}{k}\)

现在要求最长的满足 \(B\)\(A\) 前缀的 \(B\) 的长度。先求出原串的 \(\mathbf{Z}\) 函数数组 \(z\)。显然 \(B\) 也必须是整个串的前缀,所以 \(|B| \le z_{len + 1}\)。又因为 \(|B| \le |A| = \dfrac{len}{k}\),所以 \(|B|_{\max} = \min(z_{len+1},\dfrac{len}{k})\)。知道了 \(|B|\) 的范围,也就知道了满足条件的整个串的前缀的范围。开一个数组 \(d_i\),若 \(d_i > 0\) 则原串前缀 \([1,i]\) 满足条件,否则不满足,在 \(d\) 数组上将 \([i,i+|B|_{\max}]\) 整体加一。实现时差分即可。

时空复杂度均为 \(O(n)\)

代码

code
/*

p_b_p_b txdy
AThousandMoon txdy
AThousandSuns txdy
hxy txdy

*/

#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second

using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;

const int maxn = 1000100;

int n, m, nxt[maxn], fail[maxn], d[maxn];
char s[maxn];

void solve() {
	scanf("%d%d%s", &n, &m, s + 1);
	nxt[1] = n;
	for (int i = 2, l = 0, r = 0; i <= n; ++i) {
		nxt[i] = (i > r) ? 0 : min(nxt[i - l + 1], r - i + 1);
		while (s[nxt[i] + 1] == s[nxt[i] + i]) {
			++nxt[i];
		}
		if (i + nxt[i] - 1 > r) {
			l = i;
			r = i + nxt[i] - 1;
		}
	}
	for (int i = 2, j = 0; i <= n; ++i) {
		while (j && s[i] != s[j + 1]) {
			j = fail[j];
		}
		if (s[i] == s[j + 1]) {
			++j;
		}
		fail[i] = j;
	}
	for (int i = m; i <= n; i += m) {
		// printf("fail: %d\n", i - fail[i]);
		if (i % (i - fail[i]) || (i - fail[i]) * min(m, 2) > i || (i / m) % (i - fail[i])) {
			continue;
		}
		++d[i];
		--d[i + min(i / m, nxt[i + 1]) + 1];
		// printf("%d %d\n", i / m, nxt[i + 1]);
	}
	for (int i = 1; i <= n; ++i) {
		d[i] += d[i - 1];
		printf("%d", min(d[i], 1));
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}
posted @ 2022-06-22 11:20  zltzlt  阅读(44)  评论(0编辑  收藏  举报