社区讨论

SAM模板,请大佬帮忙调一下

学术版参与者 24已保存回复 28

讨论操作

快速查看讨论及其快照的属性,并进行相关操作。

当前回复
28 条
当前快照
1 份
快照标识符
@mhjgt92c
此快照首次捕获于
2025/11/04 02:20
4 个月前
此快照最后确认于
2025/11/04 06:16
4 个月前
查看原帖
CPP
#include<bits/stdc++.h>
#define N 1000010
#define INF 1000000007
using namespace std;
int n, m, ans;
string s;
int node=1, last=1, p;
int len[N], fa[N], son[N][27]; //SAM
int t[N<<2], root[N], ls[N<<2], rs[N<<2], cnt=0; //记录每个串endpos的线段树
int f[N][21], tmp[N], ip[N], position[N]; //倍增和计数排序
void update(int &p, int pos, int l, int r){
    if(!p) p = ++cnt;
    t[p]++;
    if(l==r) return ;
    int mid = l+r>>1;
    if(pos <= mid) update(ls[p], pos, l, mid);
    else update(rs[p], pos, mid+1, r);
}
int merge(int x, int y){
    if(!x||!y) return x+y;
    int nw = ++cnt;
    t[nw] = t[x]+t[y];
    ls[nw] = merge(ls[x], ls[y]);
    rs[nw] = merge(rs[x], rs[y]);
    return nw;
}
void Insert(int c){
    int np=++node;
    p = last; last = np;
    len[np] = len[p]+1;
    update(root[np], len[np], 1, n);
    //建一颗线段树,记录每个节点的endpos
    for(; !p&&!son[p][c]; p=fa[p])
        son[p][c] = np;
    if(p==0) fa[np] = 1;
    else{
        int q = son[p][c];
        if(len[q] == len[p]+1)
            fa[p] = q;
        else{
            int nq = ++node;
            len[nq] = len[p]+1;
            memcpy(son[nq], son[q], sizeof(son[q]));
            for(; !p&&son[p][c]==q; p=fa[p])
                son[p][c] = np;
            fa[nq] = fa[q];
            fa[q] = fa[np] = nq;
        }
    }
}
int query(int p, int l, int r, int L, int R){
    if(l>=L && r<=R) return t[p];
    int mid = l+r>>1, res=0;
    if(L<=mid) res += query(ls[p], l, mid, L, R);
    if(R>mid) res += query(rs[p], mid+1, r, L, R);
    return res;
}
int find_node(int x, int y){
    for(int i=20; i>=0; i--)
        if(f[x][i]&&f[x][i]>=y) x=f[x][i];
    return x;
}
bool check(int lenth, int pos, int a, int b){
    int x = find_node(pos, lenth);
    return query(x, 1, n, a+lenth-1, b);
    //如果长度为lenth的子串,结束位置在[a+lenth-1, b]中,那么s[a, b]包含这个子串
    //即s[l2, l2+mid-1]如果有endpos恰好在[a+lenth-1, b]中,长度mid就可以更新答案
}
int main()
{
    //freopen("P4094.in", "r", stdin);
    //freopen("P4094.out", "w", stdout);
    scanf("%d%d", &n, &m);
    cin >> s;
    for(int i=0; i<n; i++){
        Insert(s[i]-'a');
        position[i] = last;
    }
    for(int i=1; i<=node; i++) tmp[len[i]]++, f[i][0]=fa[i];
    for(int i=1; i<=node; i++) tmp[i] += tmp[i-1];
    for(int i=node; i>=1; i--) ip[tmp[len[i]]--] = i;
    for(int i=node; i>=1; i--) root[fa[ip[i]]] = merge(root[fa[ip[i]]], root[ip[i]]);
    //父亲是儿子节点的后缀,儿子的endpos包含于父亲的endpos,那么把儿子的线段树合并到父亲
    for(int i=1; i<=20; i++)
        for(int j=1; j<=node; j++)
            f[j][i] = f[f[j][i-1]][i-1];
    while(m--){
        int l1, r1, l2, r2;
        scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
        int L=0, R=min(r1-l1+1, r2-l2+1);
        while(L<=R){
            int mid = L+R>>1;
            if(check(mid, position[l2+mid-1], l1, r1)) L=mid+1, ans=mid;
            //我们想找s[l2, l2+mid-1],它必然在s[1, l2+mid-1]的祖先节点中(然后倍增find_node)
            else R=mid-1;
        }
        printf("%d\n", ans);
    }
    return 0;
}

回复

28 条回复,欢迎继续交流。

正在加载回复...