zoukankan      html  css  js  c++  java
  • CF547E Milk and Friends(AC自动机的fail指针上建主席树 或 广义后缀自动机的parent线段树合并)

    What-The-Fatherland is a strange country! All phone numbers there are strings consisting of lowercase English letters. What is double strange that a phone number can be associated with several bears!

    In that country there is a rock band called CF consisting of n bears (including Mike) numbered from 1 to n.

    Phone number of i-th member of CF is si. May 17th is a holiday named Phone Calls day. In the last Phone Calls day, everyone called all the numbers that are substrings of his/her number (one may call some number several times). In particular, everyone called himself (that was really strange country).

    Denote as call(i, j) the number of times that i-th member of CF called the j-th member of CF.

    The geek Mike has q questions that he wants to ask you. In each question he gives you numbers l, r and k and you should tell him the number

    Input

    The first line of input contains integers n and q (1 ≤ n ≤ 2 × 105 and 1 ≤ q ≤ 5 × 105).

    The next n lines contain the phone numbers, i-th line contains a string si consisting of lowercase English letters ().

    The next q lines contain the information about the questions, each of them contains integers l, r and k (1 ≤ l ≤ r ≤ n and 1 ≤ k ≤ n).

    Output

    Print the answer for each question in a separate line.

    Examples

    Input
    5 5
    a
    ab
    abab
    ababab
    b
    1 5 1
    3 5 1
    1 5 2
    1 5 3
    1 4 5
    Output
    7
    5
    6
    3
    6

    题意:题目给你N个字符串,Q组询问(1<=N , Q<=200000).(∑s[i] <=200000 )
    每组询问,l, r,x  : 在第 l个 字符串  到 第r个字符串这(r-l+1)个字符串中,第x个字符串出现的次数。
    
    

    题解一(后缀自动机):

     我们将这N个字符串建立SAM,然后将其parent tree拉出来,那么对于一个节点所表示的单词,他出现的次数就是他的子树的大小,由于题目是要求l~r之间,所以是他的子树中endpos在一定范围内的点的数量。我们建立SAM时,对于第k个字符串,每加入一个节点,就在对应节点的线段树里面的第k个位置加一。 我们用线段树维护每个节点的子树,然后线段树合并。

    对于每一个询问,我们只要在对应的子树里面找l~r区间的和就行了。

    参考代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define pii pair<int,int>
    #define pil pair<int,ll>
    #define mkp make_apir
    const int INF=0x3f3f3f3f;
    const int maxn=2e5+10;
    int N,Q;
    char s[maxn];
    
    int nxt[maxn<<1][26],l[maxn<<1],fa[maxn<<1];
    int last,tot,cnt[maxn<<1],c[maxn<<1];
    int sz,p[maxn],T[maxn<<1];
    
    struct Tr{
        int ls,rs;
        int num;
    } tr[maxn*40];
    
    void Update(int &x,int l,int r,int pos)
    {
        if(!x) x=++sz;
        tr[x].num++;
        if(l==r) return ;
        int mid=l+r>>1;
        if(pos<=mid) Update(tr[x].ls,l,mid,pos);
        else Update(tr[x].rs,mid+1,r,pos);
    }
    
    int Query(int x,int l,int r,int L,int R)
    {
        if(!x) return 0;
        if(L<=l&&r<=R) return tr[x].num;
        int mid=l+r>>1,res=0;
        if(L<=mid) res+=Query(tr[x].ls,l,mid,L,R);
        if(R>mid) res+=Query(tr[x].rs,mid+1,r,L,R);
        return res;
    }
    
    int Merge(int x,int y)
    {
        if(!x||!y) return x+y;
        int z=++sz;
        tr[z].ls=Merge(tr[x].ls,tr[y].ls);
        tr[z].rs=Merge(tr[x].rs,tr[y].rs);
        tr[z].num=tr[x].num+tr[y].num;
        return z;
    }
    
    void Mer()
    {
        for(int i=1;i<=tot;++i) cnt[l[i]]++;
        for(int i=1;i<=tot;++i) cnt[i]+=cnt[i-1];
        for(int i=1;i<=tot;++i) c[cnt[l[i]]--]=i;
        for(int i=tot,x;i>1;--i) x=c[i],T[fa[x]]=Merge(T[x],T[fa[x]]);
    }
    
    void Init()
    {
        last=tot=1; sz=0;
        memset(nxt[tot],0,sizeof nxt[tot]);
        l[tot]=fa[tot]=0;
    }
    
    int NewNode()
    {
        ++tot;
        memset(nxt[tot],0,sizeof nxt[tot]);
        l[tot]=fa[tot]=0;
        return tot;
    }
    
    void Insert(int ch,int x)
    {
        int p,q,np,nq;
        if(nxt[last][ch])
        {
            p=last;q=nxt[p][ch];
            if(l[q]==l[p]+1) last=q;
            else
            {
                nq=NewNode();
                l[nq]=l[p]+1;
                memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
                fa[nq]=fa[q];fa[q]=nq;
                while(p&&nxt[p][ch]==q) nxt[p][ch]=nq,p=fa[p];
                last=nq;
            }
        }
        else
        {
            np=NewNode(),p=last;
            last=np; l[np]=l[p]+1;
            while(p&&!nxt[p][ch]) nxt[p][ch]=np,p=fa[p];
            if(!p) fa[np]=1;
            else
            {
                q=nxt[p][ch];
                if(l[q]==l[p]+1) fa[np]=q;
                else
                {
                    nq=NewNode();
                    memcpy(nxt[nq],nxt[q],sizeof nxt[q]);
                    fa[nq]=fa[q];
                    l[nq]=l[p]+1;
                    fa[q]=fa[np]=nq;
                    while(p&&nxt[p][ch]==q) nxt[p][ch]=nq,p=fa[p];
                }
            }
        }
    }
    
    
    int main()
    {
        scanf("%d%d",&N,&Q);
        Init();
        for(int i=1;i<=N;++i)
        {
            scanf("%s",s);
            int len=strlen(s); last=1;
            for(int j=0;j<len;++j) Insert(s[j]-'a',i),Update(T[last],1,N,i);
            p[i]=last;
        }
        Mer();
    
        while(Q--)
        {
            int l,r,x;
            scanf("%d%d%d",&l,&r,&x);
            printf("%d
    ",Query(T[p[x]],1,N,l,r));
        }
    
        return 0;
    }
    View Code

    题解二(AC自动机):首先对于多串匹配问题,我们很容易想到AC自动机。在AC自动机的fail树中的每个节点,我们知道他出现的次数就是他的子树的大小。但是这题是要询问在第 l个 字符串  到 第r个字符串这(r-l+1)个字符串中,第x个字符串出现的次数,这我们可以想到主席树的询问历史版本信息的性质,那么我们就可以用主席树去维护AC自动机的fail指针的dfs序。然后我们只要求出第R棵树和地(L-1)棵树的差值就行了。

    参考代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define pii pair<int,int>
    #define pil pair<int,ll>
    #define mkp make_apir
    const int INF=0x3f3f3f3f;
    const int maxn=2e5+10;
    int N,Q;
    char s[maxn];
    int sz,ch[maxn][26],fail[maxn],val[maxn];
    int in[maxn],out[maxn],tim,Len[maxn],tot,T[maxn];
    vector<int> vec[maxn],ts[maxn];
    void Init()
    {
        sz=1; tim=tot=0;
        memset(ch[sz],0,sizeof ch[sz]);
        memset(val,0,sizeof val);
    }
    int idx(char c){return c-'a';}
    void Insert(char *s,int x)
    {
        Len[x]=strlen(s);int u=0;
        for(int i=0;i<Len[x];++i)
        {
            int c=idx(s[i]); ts[x].push_back(c);
            if(!ch[u][c]){memset(ch[sz],0,sizeof ch[sz]);ch[u][c]=sz++;}
            u=ch[u][c];
        }
        val[x]=u;
    }
    void GetFail()
    {
        queue<int> q;
        fail[0]=0;
        for(int i=0;i<26;++i)
            if(ch[0][i]) {fail[ch[0][i]]=0;q.push(ch[0][i]);}
    
        while(!q.empty())
        {
            int u=q.front();q.pop();
            vec[fail[u]].push_back(u);
            for(int i=0;i<26;++i)
            {
                int c=ch[u][i];
                if(!c){ch[u][i]=ch[fail[u]][i];continue;}
                q.push(c);
                fail[c]=ch[fail[u]][i];
            }
        }
    }
    void dfs(int u)
    {
        in[u]=++tim;
        for(int i=0,len=vec[u].size();i<len;++i)
            dfs(vec[u][i]);
        out[u]=tim;
    }
    
    struct Tree{
        int ls,rs;
        int num;
    } tr[maxn*40];
    
    
    inline void Update(int y,int &x,int l,int r,int pos)
    {
        tr[++tot]=tr[y];tr[tot].num++; x=tot;
        if(l==r) return;
        int mid=l+r>>1;
        if(pos<=mid) Update(tr[y].ls,tr[x].ls,l,mid,pos);
        else Update(tr[y].rs,tr[x].rs,mid+1,r,pos);
    }
    inline void Build(int y,int &x,int id)
    {
        int now=0,last=y;
        for(int i=0;i<Len[id];++i)
        {
            now=ch[now][ts[id][i]];
            Update(last,x,1,tim,in[now]);
            last=x;
        }
    }
    inline int Query(int y,int x,int l,int r,int L,int R)
    {
        if(L<=l&&r<=R) return tr[x].num-tr[y].num;
        int mid=l+r>>1,res=0;
        if(L<=mid) res+=Query(tr[y].ls,tr[x].ls,l,mid,L,R);
        if(R>mid) res+=Query(tr[y].rs,tr[x].rs,mid+1,r,L,R);
        return res;
    }
    
    int main()
    {
        scanf("%d%d",&N,&Q);
        Init();
        for(int i=1;i<=N;++i) scanf("%s",s),Insert(s,i);
        GetFail();
        dfs(0);
        for(int i=1;i<=N;++i) Build(T[i-1],T[i],i);
        while(Q--)
        {
            int l,r,x;
            scanf("%d%d%d",&l,&r,&x);
            printf("%d
    ",Query(T[l-1],T[r],1,tim,in[val[x]],out[val[x]]));
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    index of rmvb mp3 rm突破站点入口下载
    人类智商一般在多少左右?爱因斯坦的智商是多少?
    UVALive 5102 Fermat Point in Quadrangle 极角排序+找距离二维坐标4个点近期的点
    ProgressDialog使用总结
    Js中的多条件排序,多列排序
    腾讯2014年实习生招聘笔试面试经历
    周根项《一分钟速算》全集播放&amp;下载地址
    中国大推力矢量发动机WS15 跨入 世界先进水平!
    探索Android中的Parcel机制(上)
    ORACLE uuid自己主动生成主键
  • 原文地址:https://www.cnblogs.com/csushl/p/11506124.html
Copyright © 2011-2022 走看看