【题解】CF1037H Security

CF1037H Security

\(\text{Solution:}\)

代码中给出了封装好的线段树和后缀自动机。

考虑没有 \(L,R\) 限制怎么做。

显然直接在上面跑匹配:先按照 \(T\) 来跑,如果中间只能找到比他大的,就直接输出;然后是两种情况:一种是将 \(T\) 完整匹配完了,另一种是我们依靠走最小字符的串后面匹配不上了。

先考虑匹配完的情况:首先如果匹配完了,在后面如果可以接上一个最小的字符那就是答案了。先把这种情况判断掉。

那接下来上述两种情况的处理方式是一样的:考虑记录从根转移到当前失配或者是匹配结束的点的路径,考虑从后往前一个个更新是否能到达一个字典序更大的点。因为从后往前一定可以保证字典序最小。

如果找不到那就是无解,输出 \(-1.\)

那带上了限制怎么做?

那无非是在匹配的时候需要判断能不能走这个点。设当前走到这个点已经匹配的长度为 \(len,\) 则如果可以匹配,就需要满足这个点代表的 endpos 在区间 \([l+len-1,r]\) 中出现过。

那下面的问题就是如何维护每一个点的 endpos 集合了。考虑 parent 树的性质:节点的 endpos 等于其孩子的 endpos 集合之并。而后缀对应的节点的 endpos 就是后缀的位置。

于是我们可以考虑在 parent 树上进行线段树合并,维护出每一个点的 endpos 集合。注意每一个点都要有一棵自己的树。否则结构是错的。

那么注意空间问题: SAM 的空间最大达到 \(2n\) 级别,本题就是 \(2\times 10^5,\) 对应线段树合并空间约为 \(n\log n,\)\(3.6\times 10^6\) 左右。

所以一定要算好空间。

其他的细节就是计算区间和情况特判了。

动态开点的线段树不需要维护什么东西,由于我们的询问属于存在性询问,直接询问那个区间是不是有建立好的点即可。

复杂度 \(O(\sum |T|\log |S|)\)

#include<bits/stdc++.h>
using namespace std;
const int N=4e6+10;
const int SN=3e5+10;
int n,slen,Alen;
namespace SGT{
    int ls[N],rs[N],node;
    void change(int &x,const int &L,const int &R,const int &pos){
        if(!x)x=++node;
        if(L==R){return;}
        int mid=(L+R)>>1;
        if(pos<=mid)change(ls[x],L,mid,pos);
        else change(rs[x],mid+1,R,pos);
    }
    bool query(const int &x,const int &L,const int &R,const int &ql,const int &qr){
        if(!x)return 0;
        if(L>=ql&&R<=qr)return 1;
        int mid=(L+R)>>1;
        int res=0;
        if(ql<=mid)res=query(ls[x],L,mid,ql,qr);
        if(mid<qr)res|=query(rs[x],mid+1,R,ql,qr);
        return res;
    }
    int merge(const int &x,const int &y){
        if(!x||!y)return x|y;
        int p=++node;
        ls[p]=merge(ls[x],ls[y]);
        rs[p]=merge(rs[x],rs[y]);
        return p;
    }
}
using namespace SGT;
namespace SAM{
    int len[SN],pa[SN],rt[SN],tot=1,last=1,ch[SN][26],edp[SN];
    vector<int>G[SN];
    void insert(const int &c){
        int p=last;
        int np=++tot;
        last=tot;len[np]=len[p]+1;
        for(;p&&!ch[p][c];p=pa[p])ch[p][c]=np;
        if(!p)pa[np]=1;
        else{
            int q=ch[p][c];
            if(len[q]==len[p]+1)pa[np]=q;
            else{
                int nq=++tot;
                len[nq]=len[p]+1;
                pa[nq]=pa[q];pa[q]=pa[np]=nq;
                memcpy(ch[nq],ch[q],sizeof ch[q]);
                for(;p&&ch[p][c]==q;p=pa[p])ch[p][c]=nq;
            }
        }
    }
    void dfs(int x){
        if(edp[x]){
            change(rt[x],1,Alen,edp[x]);
        }
        for(auto v:G[x]){
            dfs(v);
            rt[x]=merge(rt[x],rt[v]);
        }
    }
    void BuildTree(){
        for(int i=2;i<=tot;++i)G[pa[i]].push_back(i);
        dfs(1);
    }
}
using namespace SAM;
char s[SN];
inline void print(vector<char> A){
    for(auto i:A)putchar(i);
    puts("");
}
char check(int now,char T,int len,int l,int r){
    int v=T-'a';
    for(int i=v+1;i<26;++i){
        if(ch[now][i]&&query(rt[ch[now][i]],1,Alen,l+len-1,r))return i+'a';
    }
    return '#';
}
void Mate(int l,int r){
    int now=1;
    vector<int>Path;
    vector<char>Ans;
    Path.push_back(1);
    int len=0;
    int Tag=0;
    for(int i=1;i<=slen;++i){
        int pos,ck=0;
        for(pos=s[i]-'a';pos<26;++pos){
            if(ch[now][pos]&&query(rt[ch[now][pos]],1,Alen,l+len,r)){ck=1;break;}
        }
        if(!ck){Tag=1;break;}
        Ans.push_back(pos+'a');
        if(pos!=s[i]-'a'){
            print(Ans);
            return;
        }
        Path.push_back(ch[now][pos]);
        len++;
        now=ch[now][pos];
    }
    if(Tag){
        // puts("*******");
        int plen=(int)Path.size();
        for(int i=plen-2;~i;--i){
            char lat=check(Path[i],s[len-plen+1+i+1],len-plen+1+i+1,l,r);
            Ans.pop_back();
            if(lat=='#')continue;
            else {Ans.push_back(lat);print(Ans);return;}
        }
        puts("-1");
        return;
    }
    for(int nxtpos=0;nxtpos<26;++nxtpos){
        if(ch[now][nxtpos]&&query(rt[ch[now][nxtpos]],1,Alen,l+len,r)){
            Ans.push_back(nxtpos+'a');
            print(Ans);
            return;
        }
    }
    int plen=(int)Path.size();
    for(int i=plen-2;~i;--i){
        char lat=check(Path[i],s[slen-plen+1+i+1],slen-plen+1+i+1,l,r);
        Ans.pop_back();
        if(lat=='#')continue;
        else {Ans.push_back(lat);print(Ans);return;}
    }
    puts("-1");
    return;
}
int main(){
    scanf("%s",s+1);
    slen=strlen(s+1);Alen=slen;
    for(int i=1;i<=slen;++i)insert(s[i]-'a');
    // puts("insert");
    for(int i=1,now=1;i<=slen;++i){
        int v=s[i]-'a';
        now=ch[now][v];
        edp[now]=i;
    }
    // puts("change");
    BuildTree();
    // puts("BuildTree");
    scanf("%d",&n);
    while(n--){
        int L,R;
        scanf("%d%d",&L,&R);
        scanf("%s",s+1);
        slen=strlen(s+1);
        Mate(L,R);
    }
    return 0;
}
posted @ 2021-08-22 18:47  Refined_heart  阅读(49)  评论(0编辑  收藏  举报