qko 吃串

博客针对字符串匹配问题给出题解,考虑离线逐个添加字符,将问题转化为字符串T在字符串S[1, i]中的出现次数。提出对S串建SAM,通过拓扑排序求字符串T走过去的子树大小,但此方法较暴力,可利用LCT快速求子树大小,复杂度为O(nlogn),还给出了代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解:

考虑离线然后一个一个添加字符,这样问题就转化成了字符串T在字符串S[1, i]中出现了多少次,直接对S串建SAM,然后询问只需要拓扑排序然后求字符串T走过去的子树大小即可,但是这样很明显过于暴力了。因此我们可以利用LCT来帮助我们快速求出子树大小以达到 O ( n l o g n ) O(nlogn) O(nlogn)

代码:
/*
 * @Author : Nightmare
 */
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define PII pair<int,int>
#define ls 2 * rt
#define rs 2 * rt + 1
#define gcd(a,b) __gcd(a,b)
#define eps 1e-6
#define lowbit(x) (x&(-x))
#define N 100005
#define M 100005
#define mod 1000000007
#define inf 0x3f3f3f3f
struct LCT{
    static const int maxn = 4e5 + 5;
    int sum[maxn], tag[maxn], son[maxn][2], fa[maxn], st[maxn];
    bool isroot(int x){ return son[fa[x]][0] != x && son[fa[x]][1] != x; }
    bool isright(int x){ return son[fa[x]][1] == x; }
    void add(int a, int b){ if(a) sum[a] += b, tag[a] += b; }
    void pushdown(int x){
        int lson = son[x][0], rson = son[x][1];
        if(tag[x]) add(lson, tag[x]), add(rson, tag[x]), tag[x] = 0;
    }
    void rotate(int x){
        int y = fa[x], z = fa[y], dy = isright(y), dx = isright(x);
        fa[x] = z;
        if(!isroot(y)) son[z][dy] = x;
        fa[son[x][dx ^ 1]] = y; son[y][dx] = son[x][dx ^ 1];
        fa[y] = x; son[x][dx ^ 1] = y;
    }
    void splay(int x){
        int top = 0; st[++ top] = x;
        for(int i = x ; !isroot(i) ; i = fa[i]) st[++ top] = fa[i];
        while(top) pushdown(st[top --]);
        while(!isroot(x)){
            int y = fa[x], z = fa[y];
            if(!isroot(y)) rotate((son[y][0] == x) ^ (son[z][0] == y) ? x : y);
            rotate(x);
        }
    }
    void access(int x){
        for(int y = 0 ; x ; x = fa[x]) splay(x), son[x][1] = y, y = x;
    }
    void link(int x, int y){
        fa[x] = y;
        access(y);
        splay(y);
        add(y, sum[x]);
    }
    void cut(int x){
        access(x);
        splay(x);
        add(son[x][0], -sum[x]);
        fa[son[x][0]] = 0;
        son[x][0] = 0;
    }
}lct;
struct SAM {
    static const int maxn = 2e5 + 5;
    int nxt[maxn][26], fa[maxn], len[maxn], last = 1, tot = 1;
    inline void init() {
        for (int i = 0; i <= tot; i++) fa[i] = len[i] = 0, memset(nxt[i], 0, sizeof(nxt[i]));
        last = tot = 1;
    }
    inline void insert(int x) {
        int p = last, np = ++tot;
        last = np, len[np] = len[p] + 1;
        lct.sum[np] = 1;
        for (; p && !nxt[p][x]; p = fa[p]) nxt[p][x] = np;
        if (!p) fa[np] = 1, lct.link(np, 1);
        else {
            int q = nxt[p][x];
            if (len[p] + 1 == len[q]) fa[np] = q, lct.link(np, q);
            else {
                int nq = ++tot; len[nq] = len[p] + 1;
                memcpy(nxt[nq], nxt[q], sizeof(nxt[q]));
                fa[nq] = fa[q];
                lct.link(nq, fa[q]);
                fa[q] = fa[np] = nq;
                lct.cut(q);
                lct.link(q, nq);
                lct.link(np, nq);
                for (; nxt[p][x] == q; p = fa[p]) nxt[p][x] = nq;
            }
        }
    }
    inline int query(string str){
        int rt = 1, n = str.length();
        for(int i = 0 ; i < n ; i ++){
            if(!nxt[rt][str[i] - 'a']) return 0;
            rt = nxt[rt][str[i] - 'a'];
        }
        lct.splay(rt);
        return lct.sum[rt];
    }
}sam;
char str[N]; string ss[N];
vector<PII> que[N];
int n, Q, ans[N];
void solve(){
    cin >> (str + 1); n = strlen(str + 1);
    cin >> Q;
    for(int i = 1, l, r ; i <= Q ; i ++){
        cin >> l >> r >> ss[i];
        if(ss[i].length() > r - l + 1) continue;
        que[r].emplace_back(i, 1);
        que[l + ss[i].length() - 2].emplace_back(i, -1);
    }
    sam.init();
    for(int i = 1 ; i <= n ; i ++){
        sam.insert(str[i] - 'a');
        for(auto &q : que[i]) ans[q.first] += q.second * sam.query(ss[q.first]);
    }
    for(int i = 1 ; i <= Q ; i ++) cout << ans[i] << endl;
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("D:\\in.txt", "r", stdin);
#endif
    solve();
#ifndef ONLINE_JUDGE
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值