[Codeforces1037H]Security(SAM+线段树合并)

[Codeforces1037H]Security(SAM+线段树合并)

题面

分析

CF什么时候也开始出这种套路题了
和[NOI2018]你的名字几乎一模一样,看到区间串问题,用线段树维护right集合,每次沿着转移边走的时候要判断一下转移到的节点的right集合中是否有在\([l,r]\)内的值.
因此对于每个询问串在自动机上跑求出和区间内字符的LCP.然后倒序,每次尝试加一个比当前字符大的字符,判断区间内是否存在这样的转移即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxc 26
#define maxn 500000
#define maxlogn 30
using namespace std;
int n,m;
char s[maxn+5],t[maxn+5];



struct segment_tree {
#define lson(x) (tree[x].ls)
#define rson(x) (tree[x].rs)
    struct node {
        int ls;
        int rs;
        int val;
    } tree[maxn*maxlogn+5];
    int ptr=0;
    void push_up(int x) {
        tree[x].val=tree[lson(x)].val+tree[rson(x)].val;
    }
    void update(int &x,int upos,int l,int r) {
        x=++ptr;
        if(l==r) {
            tree[x].val++;
            return;
        }
        int mid=(l+r)>>1;
        if(upos<=mid) update(lson(x),upos,l,mid);
        else update(rson(x),upos,mid+1,r);
        push_up(x);
    }
    int query(int x,int L,int R,int l,int r) {
        if(x==0||L>R) return 0;
        if(L<=l&&R>=r) return tree[x].val;
        int mid=(l+r)>>1;
        int ans=0;
        if(L<=mid) ans+=query(lson(x),L,R,l,mid);
        if(R>mid) ans+=query(rson(x),L,R,mid+1,r);
        return ans;
    }
    int merge(int x,int y) {
        if(!x||!y) return x+y;
        int now=++ptr;
        tree[now].val=tree[x].val+tree[y].val;
        lson(now)=merge(lson(x),lson(y));
        rson(now)=merge(rson(x),rson(y));
        return now;
    }
#undef lson
#undef rson
} Tr;


struct SAM {
#define link(x) t[x].link
#define len(x) t[x].len
    struct node {
        int ch[maxc];
        int link;
        int len;
        int root;
    } t[maxn*2+5];
    const int root=1;
    int last=root;
    int ptr=1;
    void extend(int c,int pos) {
        int p=last,cur=++ptr;
        len(cur)=len(p)+1;
        if(pos) Tr.update(t[cur].root,pos,1,n);
        while(p&&t[p].ch[c]==0) {
            t[p].ch[c]=cur;
            p=link(p);
        }
        if(p==0) link(cur)=root;
        else {
            int q=t[p].ch[c];
            if(len(p)+1==len(q)) link(cur)=q;
            else {
                int clo=++ptr;
                len(clo)=len(p)+1;
                for(int i=0; i<maxc; i++) t[clo].ch[i]=t[q].ch[i];
                link(clo)=link(q);
                link(q)=link(cur)=clo;
                while(p&&t[p].ch[c]==q) {
                    t[p].ch[c]=clo;
                    p=link(p);
                }
            }
        }
        last=cur;
    }
    void insert(char *s) {
        int len=strlen(s+1);
        for(int i=1; i<=len; i++) extend(s[i]-'a',i);
    }
    void topo_sort() {
        static int in[maxn+5];
        queue<int>q;
        for(int i=1; i<=ptr; i++) in[link(i)]++;
        for(int i=1; i<=ptr; i++) if(!in[i]) q.push(i);
        while(!q.empty()) {
            int x=q.front();
            q.pop();
            if(link(x)==0) continue;
            in[link(x)]--;
            t[link(x)].root=Tr.merge(t[link(x)].root,t[x].root);
            if(in[link(x)]==0) q.push(link(x));
        }
    }
    void match(char *st,int l,int r) {
        static int no[maxn+5];
        int len=strlen(st+1);
        int x=root;
        no[0]=1;
        for(int i=1; i<=len; i++) no[i]=-1;
        for(int i=1; i<=len; i++) {
            int c=st[i]-'a';
            if(t[x].ch[c]&&Tr.query(t[t[x].ch[c]].root,l,r,1,n)) x=t[x].ch[c];
            else break;
            no[i]=x;
        }
        for(int i=len; i>=0; i--) {
            if(no[i]==-1) continue;
            int c=st[i+1]-'a'+1;
            int x=no[i];
            if(i==len) c=0;
            for(int j=c; j<maxc; j++) {
                if(Tr.query(t[t[x].ch[j]].root,l+i,r,1,n)) {
                    //注意因为当前节点在i+1位,所以right的范围不是[l,r]而是[l+i,r]
                    for(int k=1; k<=i; k++) putchar(st[k]);
                    putchar(j+'a');
                    putchar('\n');
                    return;
                }
            }
        }
        puts("-1");
    }
} S;

int main() {
//  int l,r;
    scanf("%s",s+1);
    n=strlen(s+1);
    scanf("%d",&m);
    int l,r;
    S.insert(s);
    S.topo_sort();
    for(int i=1; i<=m; i++) {
        scanf("%d %d %s",&l,&r,t+1);
        S.match(t,l,r);
    }
}
/*
bacb
4
1 3 ac
*/
上一篇:luogu6139 【模板】广义后缀自动机(广义SAM)


下一篇:P4591 [TJOI2018]碱基序列 [SAM,dp]