zoukankan      html  css  js  c++  java
  • 【题解】CF1037H Security

    CF1037H Security

    ( ext{Solution:})

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

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

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

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

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

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

    那带上了限制怎么做?

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

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

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

    那么注意空间问题: SAM 的空间最大达到 (2n) 级别,本题就是 (2 imes 10^5,) 对应线段树合并空间约为 (nlog n,)(3.6 imes 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;
    }
    
  • 相关阅读:
    java 锁
    mybatis 基本用法
    MYSQL 数据库
    进程与线程的区别
    EJB类型
    线程创建方式
    JDBC连接数据库的基本步骤
    org.elasticsearch.bootstrap.StartupException: java.lang.RuntimeException: can not run elasticsearch
    Centos中的/etc/sysconfig/network-scripts/ifcfg-eth0的正确配置
    利用maven导入依赖失败的问题
  • 原文地址:https://www.cnblogs.com/h-lka/p/15173160.html
Copyright © 2011-2022 走看看