1 条题解

  • 0
    @ 2025-8-24 23:00:34

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar EuphoricStar
    Remember.

    搬运于2025-08-24 23:00:34,当前版本为作者最后更新于2024-07-09 20:13:57,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    一个 AA 合法的充要条件为:

    • AAS1iS_{1 \sim i} 的一个 border;
    • AAS1iS_{1 \sim i} 中不重叠地出现 k\ge k 次。

    建出失配树后,发现合法的 AA 在树上组成一条某个点 uu 到根的链,且 uuii 的祖先。因此我们若知道 uu,答案就是 depudep_u

    考虑倍增求出 uu。相当于要 check 这样一个问题:

    • S1uS_{1 \sim u} 的第 kk 次不重叠出现位置是否 i\le i

    考虑预处理出每个前缀 S1uS_{1 \sim u} 对应的串在整个串中的不重叠出现位置。相当于每次找到一个位置后面第一个后缀,使得它与整个串的 LCP 长度 u\ge u,然后跳到它。跳的步数最多是 i=1nni=O(nlogn)\sum\limits_{i = 1}^n \frac{n}{i} = O(n \log n) 所以可以暴力跳。

    一个后缀与整个串的 LCP 长度可以想到 Z 函数。用并查集维护链表的 trick,从小到大枚举 uu,处理完 uu 后把 zp=uz_p = u 的所有位置 pp 删了,使得处理到 uu 时,并查集中 zpuz_p \ge u 的位置为代表元。这样即可 O(α(n))O(\alpha(n)) 求出一个位置后面第一个 pp 使得 zpuz_p \ge upp

    注意特判 k=1k = 1

    总时间复杂度 O(qlogn+nα(n)logn)O(q \log n + n \alpha(n) \log n)

    #include <bits/stdc++.h>
    #define pb emplace_back
    #define fst first
    #define scd second
    #define mkp make_pair
    #define mems(a, x) memset((a), (x), sizeof(a))
    
    using namespace std;
    typedef long long ll;
    typedef double db;
    typedef unsigned long long ull;
    typedef long double ldb;
    typedef pair<ll, ll> pii;
    
    const int maxn = 200100;
    const int logn = 20;
    
    int n, m, fail[maxn], dep[maxn], z[maxn], st[logn][maxn], f[logn][maxn], fa[maxn];
    char s[maxn];
    vector<int> vc[maxn], cv[maxn];
    
    int find(int x) {
    	return fa[x] == x ? x : fa[x] = find(fa[x]);
    }
    
    void solve() {
    	scanf("%d%s%d", &n, s + 1, &m);
    	for (int i = 2, j = 0; i <= n; ++i) {
    		while (j && s[i] != s[j + 1]) {
    			j = fail[j];
    		}
    		j += (s[i] == s[j + 1]);
    		fail[i] = j;
    	}
    	for (int i = 1; i <= n; ++i) {
    		f[0][i] = fail[i];
    		dep[i] = dep[fail[i]] + 1;
    	}
    	for (int j = 1; j <= 19; ++j) {
    		for (int i = 1; i <= n; ++i) {
    			f[j][i] = f[j - 1][f[j - 1][i]];
    		}
    	}
    	z[1] = n;
    	for (int i = 2, l = 0, r = 0; i <= n; ++i) {
    		if (i <= r) {
    			z[i] = min(z[i - l + 1], r - i + 1);
    		}
    		while (i + z[i] <= n && s[z[i] + 1] == s[i + z[i]]) {
    			++z[i];
    		}
    		if (i + z[i] - 1 > r) {
    			l = i;
    			r = i + z[i] - 1;
    		}
    	}
    	for (int i = 1; i <= n; ++i) {
    		cv[z[i]].pb(i);
    		fa[i] = (z[i] ? i : i + 1);
    	}
    	fa[n + 1] = n + 1;
    	for (int i = 1; i <= n; ++i) {
    		vc[i].pb(i);
    		int p = i;
    		while (p + i <= n) {
    			int q = find(p + 1);
    			if (q == n + 1) {
    				break;
    			}
    			p = q + i - 1;
    			vc[i].pb(p);
    		}
    		for (int j : cv[i]) {
    			fa[j] = j + 1;
    		}
    	}
    	while (m--) {
    		int x, y;
    		scanf("%d%d", &x, &y);
    		if (y == 1) {
    			puts("1");
    			continue;
    		}
    		int t = x;
    		for (int i = 19; ~i; --i) {
    			if (f[i][x] && ((int)vc[f[i][x]].size() < y || vc[f[i][x]][y - 1] > t)) {
    				x = f[i][x];
    			}
    		}
    		x = f[0][x];
    		printf("%d\n", dep[x]);
    	}
    }
    
    int main() {
    	int T = 1;
    	// scanf("%d", &T);
    	while (T--) {
    		solve();
    	}
    	return 0;
    }
    
    
    • 1

    【MX-X1-T4】「KDOI-05」简单的字符串问题

    信息

    ID
    10272
    时间
    2000ms
    内存
    512MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者