parent 树上启发式合并

parent 树上启发式合并

题目描述

$LH$ 拿到了一个字符串 $S$,他想要和你玩个游戏,每次 $LH$ 询问你一个字符串 $T$,和一个整数 $k$,你需要回答,字符串 $T$ 在 $S$ 中从左到右出现第 $k$ 次的位置在哪。

假如 $S[l \sim r]=T$,那么字符串 $T$ 在位置 $r$ 出现。

字符串中只可能出现小写英文字母,大写英文字母和阿拉伯数字。

输入描述:

第一行输入两个整数 $n(1 \leq n \leq 10^5 ),q(1 \leq q \leq 10^5)$,分别表示 $S$ 的长度和询问次数。

第二行包含一个字符串 $S$。

随后 $q$ 行,每行包含一个字符串 $T$ 和一个正整数 $k(1 \leq k \leq 10^5)$。

保证所有询问的不同的字符串 $T$ 的长度和不超过 $10^4$ 。

保证所有询问的字符串 $T$ 的长度和不超过 $10^5$ 。

字符串可能包含英文小写字母,英文大写字母,数字。

提示:请注意不寻常的内存限制。

输出描述:

对于每个询问,输出 $T$ 在 $S$ 中出现第 $k$ 次的下标,假如 $T$ 在 $S$ 中的出现次数少于 $k$ 次,请输出 $−1$。

示例1

输入

10 5
abcabC8dab
ab 3
abc 1
e 1
b 2
abced 2

输出

10
3
-1
5
-1

 

解题思路

  显然我们不可能单独处理每个询问。本题关键的地方在于,要对询问的字符串长度分类处理。

  假设询问的字符串总长度为 $m$,那么字符串长度的种类最多有 $O(\sqrt{m})$ 种。此时我们就可以反过来考虑,进行离线处理。枚举这 $O(\sqrt{m})$ 种长度,对于长度 $\text{len}$,将字符串 $s$ 中所有长度为 $\text{len}$ 的子串的出现位置存储下来。然后枚举所有的询问,如果字符串 $t$ 的出现位置至少记录了 $k$ 次,则找到了该询问的答案。

  另外为了方便映射子串对应的出现位置,以及进行字符串比较,需要对 $s$ 和每个 $t$ 进行字符串哈希。

  AC 代码如下,时间复杂度为 $O((n+q)\sqrt{m}\log{n})$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

const int N = 1e5 + 5, P = 13331;

char s[N];
ULL h[N], p[N];
array<ULL, 2> q[N];
set<int> st[N];
int ans[N];

ULL query(int l, int r) {
    return h[r] - h[l - 1] * p[r - l + 1];
}

int main() {
    int n, m;
    scanf("%d %d %s", &n, &m, s + 1);
    p[0] = 1;
    for (int i = 1; i <= n; i++) {
        h[i] = h[i - 1] * P + s[i];
        p[i] = p[i - 1] * P;
    }
    for (int i = 0; i < m; i++) {
        int x;
        scanf("%s %d", s, &x);
        int len = strlen(s);
        ULL t = 0;
        for (int i = 0; i < len; i++) {
            t = t * P + s[i];
        }
        q[i] = {t, ULL(x)};
        st[len].insert(t);
    }
    for (int i = 1; i < N; i++) {
        if (st[i].empty()) continue;
        map<ULL, vector<int>> mp;
        for (int j = i; j <= n; j++) {
            ULL t = query(j - i + 1, j);
            if (st[i].count(t)) mp[t].push_back(j);    // 需要加上该的判断剪枝,否则会超时
        }
        for (int i = 0; i < m; i++) {
            if (q[i][1] <= mp[q[i][0]].size()) ans[i] = mp[q[i][0]][q[i][1] - 1];
        }
    }
    for (int i = 0; i < m; i++) {
        printf("%d\n", ans[i] ? ans[i] : -1);
    }
    
    return 0;
}

  最小代价构造字符串图上计数(Hard) 都用到了类似思想。

 

参考资料

  牛客小白月赛97题解:https://www.nowcoder.com/discuss/636309017548120064

posted @ 2024-07-18 16:11  onlyblues  阅读(17)  评论(0)    收藏  举报
Web Analytics