parent 树上启发式合并

parent 树上启发式合并

题目描述

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

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

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

输入描述:

第一行输入两个整数 n(1n105),q(1q105),分别表示 S 的长度和询问次数。

第二行包含一个字符串 S

随后 q 行,每行包含一个字符串 T 和一个正整数 k(1k105)

保证所有询问的不同的字符串 T 的长度和不超过 104

保证所有询问的字符串 T 的长度和不超过 105

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

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

输出描述:

对于每个询问,输出 TS 中出现第 k 次的下标,假如 TS 中的出现次数少于 k 次,请输出 1

示例1

输入

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

输出

10
3
-1
5
-1

 

解题思路

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

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

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

  AC 代码如下,时间复杂度为 O((n+q)mlogn)

#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 @   onlyblues  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示