zoukankan      html  css  js  c++  java
  • [NOI2018]你的名字

    SAM写的太不熟练了~~SAM上的线段树合并也不熟练~~~

    调了半天样例

    题目大意:

    给定一个S,Q次询问,每次给出T,l,r,

    求对于S[l,r],属于T的子串却不属于S[l,r]的子串有多少个

    看上去挺简洁的一个问题。。。

    暴力68pts

    对于S[1,n]68pts?

    如果做过

     [HEOI2015]最短不公共子串

    就好做多了!

    可以对A,B分别建SAM

    拓扑排序找到A中每个点的后面路径条数。

    然后在A上面匹配一遍,如果B匹配不出,直接加上A后面的路径条数

    100pts?

    刚才的暴力方法实际上不适用了

    因为DAG根本无法精确找到[l,r]的部分。。

     

    换一个角度

    不从图的路径角度考虑子串了

    直接从子串定义考虑

     

    考虑,对于T,[1,i]这个前缀贡献的答案

    假设同一个子串可以算多次的话

    把[1,i]这个前缀在S[l,r]中匹配,设最长长度是mx

    那么贡献的答案就是i-mx

     

    怎么计算"把[1,i]这个前缀在S[l,r]中匹配"得到的最长后缀长度?

    用线段树合并维护S的SAM中,点P的right集合

    设[1,i-1]匹配的长度为now,匹配在SAM上的点为p

    如果p有c出点,出点是x

    如果x的right集合中有[l+now,r]区间中一个元素,意味着可以直接匹配下去,得到最长的长度了。break

    否则now--,继续尝试。如果now==len[fa[p]],可以更新到更大的集合了,p=fa[p]

    设i前缀匹配长度为lim[i]

     upda:2019.3.8:

    这个匹配本质上是不断找到当前可能的最长后缀now+'c'在S中所有出现位置,然后看这些出现位置有没有末尾在[l+now,r]的

    至于相同的子串是1个

    那么对T串再建立SAM,用parent树去重,parent树上dfs,每个点的贡献是max(0,min(len[x]-len[fa[x]],len[x]-lim[x]))

    相当于把同构的串放在一起,只计算一次

    代码

    注意,

    1.线段树合并还要支持之后的查询

    所以必须每次新建节点

    类似:CF666E Forensic Examination

    2.tot,cnt,num计数器很多别混(懒得namespace了)

    #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=1e6+5;
    const int M=1e6+5;
    int n,q;
    char s[N];
    int lim[M];
    ll ans;
    struct SAMSAM{
        int ch[N][26];
        int len[N],nd,fa[N];
        int cnt;
        void init(){
            cnt=1,nd=1;
        }
        struct tr{
            int ls,rs;
            int sum;
        }t[N*20];
        int rt[N];
        int tot;
        void pushup(int x){
            t[x].sum=t[t[x].ls].sum+t[t[x].rs].sum;
        }
        void upda(int &x,int l,int r,int to){
            x=++tot;
            if(l==r) {
                t[x].sum=1;return;
            }
            if(to<=mid) upda(t[x].ls,l,mid,to);
            else upda(t[x].rs,mid+1,r,to);
            pushup(x);
        }
        int merge(int x,int y,int l,int r){
        //    cout<<" merging "<<x<<" "<<y<<" :: "<<l<<" "<<r<<endl;
            if(!x||!y) return x+y;
            int id=++tot;
            if(l==r){
                t[id].sum=t[y].sum+t[x].sum;
                return id;
            }
            t[id].ls=merge(t[x].ls,t[y].ls,l,mid);
            t[id].rs=merge(t[x].rs,t[y].rs,mid+1,r);
            pushup(id);
            return id;
        }
        void ins(int c,int l){
            int p=nd;len[nd=++cnt]=l;
            upda(rt[cnt],1,n,l);
            
            for(;p&&ch[p][c]==0;p=fa[p]) ch[p][c]=cnt;
        //    cout<<"pp "<<p<<" cnt "<<cnt<<" char "<<c<<" ll "<<l<<" : "<<ch[1][c]<<endl;
            if(!p){
                fa[cnt]=1;
                return;
            }
            int q=ch[p][c];
            if(len[q]==len[p]+1){
                fa[cnt]=q;
                return;
            }
            len[++cnt]=len[p]+1;
            fa[cnt]=fa[q];fa[q]=fa[nd]=cnt;
            for(reg j=0;j<26;++j) ch[cnt][j]=ch[q][j];
            for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=cnt;
        }
        struct edge{
            int nxt,to;
        }e[2*N];
        int hd[2*N],num;
        void add(int x,int y){
            e[++num].nxt=hd[x];
            e[num].to=y;
            hd[x]=num;
        }
        void build(){
        //    cout<<" cnt "<<cnt<<endl;
            for(reg i=2;i<=cnt;++i){
        //        cout<<i<<" : fafa "<<fa[i]<<endl;
                add(fa[i],i);
            }
        }
        void dfs(int x){
        //    cout<<" xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx "<<x<<" rt "<<rt[x]<<" sz "<<t[x].sum<<endl;
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
                dfs(y);
                rt[x]=merge(rt[x],rt[y],1,n);
            }
        }
        int query(int x,int l,int r,int L,int R){
        //    cout<<" xx "<<x<<" "<<l<<" and "<<r<<" : query "<<L<<" "<<R<<"  sz "<<t[x].sum<<endl;
            if(l>r) return 0;
            if(!x) return 0;
            if(L<=l&&r<=R) return t[x].sum;
            int ret=0;
            if(L<=mid) ret+=query(t[x].ls,l,mid,L,R);
            if(mid<R) ret+=query(t[x].rs,mid+1,r,L,R);
            return ret; 
        }
    }SAM;
    struct samsam{
        int ch[M][26];
        int len[M],nd,fa[M];
        int mx[M];
        int cnt;
        void init(){
            cnt=1,nd=1;
        }
        void ins(int c,int l){
            int p=nd;len[nd=++cnt]=l;
            mx[cnt]=lim[l];//warning!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            
            for(;p&&ch[p][c]==0;p=fa[p]) ch[p][c]=cnt;
            if(!p){
                fa[cnt]=1;
                return;
            }
            int q=ch[p][c];
            if(len[q]==len[p]+1){
                fa[cnt]=q;
                return;
            }
            len[++cnt]=len[p]+1;
            fa[cnt]=fa[q];fa[q]=fa[nd]=cnt;
            for(reg j=0;j<26;++j) ch[cnt][j]=ch[q][j];
            for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=cnt;
        }
        struct edge{
            int nxt,to;
        }e[2*N];
        int hd[2*N],num;
        void add(int x,int y){
            e[++num].nxt=hd[x];
            e[num].to=y;
            hd[x]=num;
        }
        void build(){
            for(reg i=2;i<=cnt;++i){
                add(fa[i],i);
            }
        }
        void dfs(int x){////////////////////////////////////////////////////////ans ansnsnannsansansna asn ans
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
                dfs(y);
                mx[x]=max(mx[x],mx[y]);
            }
            ans+=max(0,min(len[x]-mx[x],len[x]-len[fa[x]]));
        }
        void clear(){
            for(reg i=1;i<=cnt;++i){
                for(reg j=0;j<26;++j){
                    ch[i][j]=0;
                }
                mx[i]=0;len[i]=0;
                hd[i]=0;
                fa[i]=0;
            }
            num=0;
            cnt=1;
        }
    }sam;
    void clear(){
        sam.clear();
        ans=0;
    }
    int main(){
        scanf("%s",s+1);
        n=strlen(s+1);
        SAM.init();
        for(reg i=1;i<=n;++i){
            SAM.ins(s[i]-'a',i);
        }
    //    cout<<" after ins "<<endl;
        SAM.build();
    //    cout<<" after build "<<endl;
        SAM.dfs(1);
    //    cout<<" after dfs "<<endl;
        rd(q);
        int l,r;
        while(q--){
            clear();
            scanf("%s",s+1);
            rd(l);rd(r);
            int len=strlen(s+1);
            
            int now=0,p=1;
            for(reg i=1;i<=len;++i){//pipei 
                int c=s[i]-'a';
                while(1){
        //            cout<<" cc "<<c<<" "<<SAM.ch[p][c]<<endl;
                    if(SAM.ch[p][c]&&SAM.query(SAM.rt[SAM.ch[p][c]],1,n,l+now,r)){
                        ++now;
                        p=SAM.ch[p][c];
                        break;
                    }    
                    if(!now) break;
                    --now;
                    if(now==SAM.len[SAM.fa[p]]) p=SAM.fa[p];
                }
                
                lim[i]=now;
        //        cout<<" lim "<<i<<" : "<<lim[i]<<" p "<<p<<endl;
            }
            
            sam.init();
            for(reg i=1;i<=len;++i){//insert
                sam.ins(s[i]-'a',i);
            }
            ans=0;
            sam.build();
            sam.dfs(1);
            printf("%lld
    ",ans);
        }
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2019/1/18 17:48:14
    */

    总结:

    SAM对于公共子串问题一个基本的方法是跑上去匹配

    然后下来再考虑每个位置的贡献

    parent树、DAG图无形对子串进行了同构的去重

  • 相关阅读:
    统计学习方法学习笔记(一)--极大似然估计与贝叶斯估计原理及区别
    数据过拟合解决方法
    LSTM基础
    异方差产生与解决
    人工免疫相关算法
    Svm相关
    sscanf,sscanf_s及其相关用法
    C语言数组初始化
    生产者和消费者
    Linux线程-创建
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10289785.html
Copyright © 2011-2022 走看看