• CF666E Forensic Examination


    https://www.luogu.org/problemnew/show/CF666E

    本质还是一个串在一些串里的匹配

    考虑对模板串建广义SAM

    既然有S的一些匹配关系的询问,套路地,

    把S在上面跑,

    pi[i]记录S的[1~i]的前缀在SAM上的匹配长度

    pos[i]记录S的[1~i]的前缀在SAM的匹配最终位置

    询问的时候,如果匹配长度不够l,那么输出(L,0)

    从pos[i]往上倍增,直到len刚好大于pr-pl+1(开始的时候,如果匹配长度不够,直接返回0)

    整个子树对应的叶子就是所有出现位置。

    没有的话,还要检查,输出(L,0)

    至于在[l,r]哪个串出现位置最多,线段树合并即可。

    或者dsu on the tree(即区间众数) 拿两个桶维护一下。

    一个维护每个值出现次数,以及出现次数为某个次数的值有多少种。

    代码细节:

    1.线段树合并:由于之前的还要用,所以合并的时候务必新建一个节点,空间变成2倍,但是本质上参与合并的点数还是nlogn的。

    否则类似主席树,之前的值会改变,无法查询!

    2.记录匹配长度是在S的位置记录!不是一般的在SAM上记录最长匹配位置!(这个简直大错特错,因为可能和原子串根本不符合匹配!长度大于,但是可能是S别的地方匹配过来的!)

    3.pi数组的长度是N,不是M(N,M)并不同阶。

    代码:

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define mid ((l+r)>>1)
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=5e5+5;
    const int M=1e5+5;
    int n,m;
    char s[N],a[M];
    pair<int,int> cmp(pair<int,int>a,pair<int,int>b){
        if(a.second==b.second) {
            return a.first<b.first?a:b;
        }    
        return a.second>b.second?a:b;
    }
    struct SAM{
        int f[2*M][20],ch[2*M][26],len[2*M];
        int nd,cnt;
        void init(){
            nd=cnt=1;
        }
        struct node{
            int ls,rs;
            int mx,id;
        }t[4*M*20];
        int rt[2*M];
        int pos[N];
        int pi[N];
        int num;
        void pushup(int x){
            if(t[t[x].ls].mx>t[t[x].rs].mx||(t[t[x].ls].mx==t[t[x].rs].mx&&t[t[x].ls].id<t[t[x].rs].id)){
                t[x].mx=t[t[x].ls].mx;
                t[x].id=t[t[x].ls].id;
            }
            else{
                t[x].mx=t[t[x].rs].mx;
                t[x].id=t[t[x].rs].id;
            }
        }
        void upda(int &x,int l,int r,int to,int c){
            if(!x) x=++num;
            if(l==r){
                t[x].mx+=c;t[x].id=l;return;
            }
            if(to<=mid) upda(t[x].ls,l,mid,to,c);
            else upda(t[x].rs,mid+1,r,to,c);
            pushup(x);
        }
        int merge(int x,int y,int l,int r){
            if(!x||!y) return x+y;
            int ret=++num;
            if(l==r){
                t[ret].mx=t[x].mx+t[y].mx;
                t[ret].id=t[x].id;
                return ret;
            }
            t[ret].ls=merge(t[x].ls,t[y].ls,l,mid);
            t[ret].rs=merge(t[x].rs,t[y].rs,mid+1,r);
            pushup(ret);
            return ret;
        }
        pair<int,int> query(int x,int l,int r,int L,int R){
            //cout<<" quering "<<l<<" "<<r<<" "<<L<<" "<<R<<" :: "<<t[x].id<<" "<<t[x].mx<<endl;
            if(L<=l&&r<=R){
                return make_pair(t[x].id,t[x].mx);
            }
            pair<int,int>ret;
            ret.first=0,ret.second=-2333;
            if(L<=mid) ret=cmp(ret,query(t[x].ls,l,mid,L,R));
            if(mid<R) ret=cmp(ret,query(t[x].rs,mid+1,r,L,R));
            return ret;
        }
        void ins(int l,int c,int d){
            int p=nd;nd=++cnt;len[nd]=l;
            for(;p&&ch[p][c]==0;p=f[p][0]) ch[p][c]=cnt;
            int q;
            if(!p){
                f[nd][0]=1;goto abc;
            }
            q=ch[p][c];
            if(len[q]==len[p]+1){
                f[nd][0]=q;goto abc;
            }
            len[++cnt]=len[p]+1;
            f[cnt][0]=f[q][0];f[q][0]=f[nd][0]=cnt;
            for(reg i=0;i<26;++i) ch[cnt][i]=ch[q][i];
            for(;p&&ch[p][c]==q;p=f[p][0]) ch[p][c]=cnt;
            abc:;
            upda(rt[nd],1,m,d,1);
        }
        void wrk(char *s){
            int lth=strlen(s+1);
            int now=1;
            int l=0;
            for(reg i=1;i<=lth;++i){
                int x=s[i]-'a';
            //    cout<<" wrk "<<i<<" "<<now<<" "<<l<<endl;
                if(ch[now][x]==0){
                    while(now&&ch[now][x]==0) now=f[now][0];
                    if(!now){
                        l=0;
                        pos[i]=1;
                        now=1;pi[i]=0;
                    }
                    else{
                        l=len[now]+1;
                        now=ch[now][x];
                        pi[i]=l;pos[i]=now;
                    }
                }else{
            //        cout<<" ok "<<endl;
                    ++l;
                    now=ch[now][x];
                    pi[i]=l;pos[i]=now;
                }
            }
        }
        struct edge{
            int nxt,to;
        }e[2*M];
        int hd[2*M],tot;
        void add(int x,int y){
            e[++tot].nxt=hd[x];
            e[tot].to=y;
            hd[x]=tot;
        }
        void dfs(int x){
            //cout<<" xx "<<x<<" fa "<<f[x][0]<<endl;
            //cout<<" rt[21] "<<rt[21]<<" "<<t[rt[21]].id<<" "<<t[rt[21]].mx<<endl;
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
                dfs(y);
                //cout<<" mergeing "<<rt[x]<<" "<<rt[y]<<endl;
            //    cout<<" before  rt[21] "<<rt[21]<<" "<<t[rt[21]].id<<" "<<t[rt[21]].mx<<endl;
                rt[x]=merge(rt[x],rt[y],1,m);
                //cout<<" after rt[21] "<<rt[21]<<" "<<t[rt[21]].id<<" "<<t[rt[21]].mx<<endl;
            }
        }
        void build(){//add_edge+dfs+merge+beizeng
            for(reg i=2;i<=cnt;++i) add(f[i][0],i);
            dfs(1);
            for(reg j=1;j<=19;++j){
                for(reg i=1;i<=cnt;++i){
                    f[i][j]=f[f[i][j-1]][j-1];
                }
            }
        }
        pair<int,int>sol(int p,int l,int L,int R,int lp){
            //if(lp==53) cout<<" p "<<p<<" ll "<<l<<" : "<<pos[p]<<" "<<pi[pos[p]]<<" L R "<<L<<" "<<R<<" lp "<<lp<<endl;
            if(pi[p]<l) return make_pair(L,0);
            p=pos[p];
            for(reg j=19;j>=0;--j){
                if(len[f[p][j]]>=l) p=f[p][j];
            }
            //if(lp==53) cout<<" after jump "<<p<<" "<<len[p]<<" : "<<f[p][0]<<" "<<len[f[p][0]]<<" "<<rt[p]<<endl;
            pair<int,int>ret=query(rt[p],1,m,L,R);
            if(ret.first==0) ret.first=L;
            return ret;
        }
        void main(){
            scanf("%s",s+1);
            //num=1;
            //rt[1]=1;
            
            rd(m);
            init();
            for(reg i=1;i<=m;++i){
                int now=0;
                scanf("%s",a+1);
                now=strlen(a+1);
                nd=1;
                for(reg j=1;j<=now;++j){
                    ins(j,a[j]-'a',i);
                }
                //cout<<" rt[2] "<<rt[2]<<" "<<t[rt[2]].id<<" "<<t[rt[2]].mx<<endl;
            }
            wrk(s);
            //int lll=strlen(s+1);
    //        for(reg i=1;i<=lll;++i){
    //            cout<<i<<" : "<<pos[i]<<endl;
    //        }
            build();
            //cout<<" rt[2] "<<rt[2]<<" "<<t[rt[2]].id<<" "<<t[rt[2]].mx<<endl;
            
            int q;
            rd(q);
            int l,r,pl,pr;
            int o=0;
            while(q--){
                rd(l);rd(r);rd(pl);rd(pr);
                pair<int,int>ans=sol(pr,pr-pl+1,l,r,++o);
                printf("%d %d
    ",ans.first,ans.second);
            }
        }
    }sam;
    int main(){
        sam.main();
        return 0;
    }
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/12/31 10:57:37
    */

    总结:
    思路还是简单自然的

    要多串的子串匹配,就广义SAM

    询问和S子串有关,就先跑一下匹配

    然后询问,考虑结尾位置,然后倍增,子树出现标记?线段树合并!

    细节还是要注意。

  • 相关阅读:
    Linux 第一个脚本程序
    Linux 8开启FTP功能服务
    PPT 倒计时时钟,用 GIF 动画实现,可直接使用 -- 附 Python 实现代码
    python flask 虚拟环境迁移
    GOLANG学习之路之二
    Golang学习之路之一
    vscode 调试flask项目 解决(socket.gaierror: [Errno 11001] getaddrinfo failed)
    windows下部署 flask (win10+flask+nginx)
    git入门
    配置maven的国内镜像
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10201475.html
Copyright © 2020-2023  润新知