Codeforces 666E Forensic Examination (後綴自動機+線段樹合併)

E. Forensic Examination

The country of Reberland is the archenemy of Berland. Recently the authorities of Berland arrested a Reberlandian spy who tried to bring the leaflets intended for agitational propaganda to Berland illegally . The most leaflets contain substrings of the Absolutely Inadmissible Swearword and maybe even the whole word.

Berland legal system uses the difficult algorithm in order to determine the guilt of the spy. The main part of this algorithm is the following procedure.

All the m leaflets that are brought by the spy are numbered from 1 to m. After that it’s needed to get the answer to q queries of the following kind: “In which leaflet in the segment of numbers [l, r] the substring of the Absolutely Inadmissible Swearword [pl, pr] occurs more often?”.

The expert wants you to automate that procedure because this time texts of leaflets are too long. Help him!

Input

The first line contains the string s (1 ≤ |s| ≤ 5·105) — the Absolutely Inadmissible Swearword. The string s consists of only lowercase English letters.

The second line contains the only integer m (1 ≤ m ≤ 5·104) — the number of texts of leaflets for expertise.

Each of the next m lines contains the only string ti — the text of the i-th leaflet. The sum of lengths of all leaflet texts doesn’t exceed 5·104. The text of the leaflets consists of only lowercase English letters.

The next line contains integer q (1 ≤ q ≤ 5·105) — the number of queries for expertise.

Finally, each of the last q lines contains four integers l, r, pl, pr (1 ≤ l ≤ r ≤ m, 1 ≤ pl ≤ pr ≤ |s|), where |s| is the length of the Absolutely Inadmissible Swearword.

Output

Print q lines. The i-th of them should contain two integers — the number of the text with the most occurences and the number of occurences of the substring [pl, pr] of the string s. If there are several text numbers print the smallest one.

input

suffixtree
3
suffixtreesareawesome
cartesiantreeisworsethansegmenttree
nyeeheeheee
2
1 2 1 10
1 3 9 10

output

1 1
3 4


題目大意:
給定一個字符串s ,和m 個字符串p1.......pmq 次詢問,每次詢問在pl......pr 中,s 的一個子串s[x...y] 在哪一個串中出現次數最多。


詢問子串的出現次數,考慮構建後綴自動機,由於有多個串,因此需要分開維護Right 集合的大小,構建串s+@+p1+@+......+pm 的後綴自動機。對於每個節點,維護一顆線段樹,表示該節點表示的子串在每一個p 中出現的次數,這個在每次添加新的np 節點時直接往線段樹中插入就行。

自動機建好了之後按照parent 樹,自底向上將子節點的線段樹合併到父節點上,利用可持久化線段樹可以完成。

然後對於每次詢問,預先記錄每個s 每個前綴在自動機上的位置,然後在parent 樹上倍增查找子串對應節點,找到對應節點後直接在線段樹上詢問區間[l,r] 的最大值即可。

總時間複雜度O(nlog2n) ,注意在合併線段樹的時候一定要新開節點。

同時這題有個坑點就是當出現次數是0 時,直接輸出查詢的左界。


代碼:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 1200000
#define M 20000000
using namespace std;
typedef pair<int,int> par;
namespace Seg
{
    int rt[N],ls[M],rs[M],Max[M],id[M],tot;
    int CP(int p)
    {
        int o=++tot;
        ls[o]=ls[p];
        rs[o]=rs[p];
        Max[o]=Max[p];
        id[o]=id[p];
        return o;
    }
    void UD(int p)
    {
        Max[p]=max(Max[ls[p]],Max[rs[p]]);
        if(Max[ls[p]]<Max[rs[p]])id[p]=id[rs[p]];
        else id[p]=id[ls[p]];
    }
    int ADD(int p,int l,int r,int k,int d)
    {
        int o=CP(p);
        if(l==r)return Max[o]+=d,id[o]=k,o;
        int mid=l+r>>1;
        if(k<=mid)ls[o]=ADD(ls[o],l,mid,k,d);
        else rs[o]=ADD(rs[o],mid+1,r,k,d);
        UD(o);return o;
    }
    par GS(int p,int l,int r,int x,int y)
    {
        if(Max[p]==0)return par(0,max(l,x));
        if(x<=l&&y>=r)return par(Max[p],id[p]);
        int mid=l+r>>1;par t1=par(-1,0),t2=par(-1,0);
        if(x<=mid&&y>=l)t1=GS(ls[p],l,mid,x,y);
        if(x<=r&&y>mid)t2=GS(rs[p],mid+1,r,x,y);
        if(t1.first>=t2.first)return t1;
        return t2;
    }
    int Merge(int p1,int p2,int l,int r)
    {
        if(!p1)return CP(p2);
        if(!p2)return CP(p1);
        int o=++tot,mid=l+r>>1;
        if(l==r)return Max[o]=Max[p1]+Max[p2],id[o]=l,o;
        ls[o]=Merge(ls[p1],ls[p2],l,mid);
        rs[o]=Merge(rs[p1],rs[p2],mid+1,r);
        UD(o);return o;
    }
}
char s[N];
int TOT,LA[N],NE[N],EN[N],S=21;
int m,q,tot=1,rt=1,las=1,son[N][27],pra[N],Max[N],en[N],fa[N][22];
void ADD(int x,int y)
{
    TOT++;
    EN[TOT]=y;
    NE[TOT]=LA[x];
    LA[x]=TOT;
}
int NP(int x)
{
    Max[++tot]=x;
    return tot;
}
void Ins(int t,int ty)
{
    int p=las,np,q,nq;
    np=NP(Max[p]+1);
    if(ty)Seg::rt[np]=Seg::ADD(Seg::rt[np],1,m,ty,1);
    while(p&&!son[p][t])son[p][t]=np,p=pra[p];
    if(!p)pra[np]=rt;
    else
    {
        q=son[p][t];
        if(Max[q]==Max[p]+1)pra[np]=q;
        else
        {
            nq=NP(Max[p]+1);
            memcpy(son[nq],son[q],sizeof(son[q]));
            pra[nq]=pra[q];
            pra[q]=pra[np]=nq;
            while(son[p][t]==q)son[p][t]=nq,p=pra[p];
        }
    }
    las=np;
}
void DFS(int x,int f)
{
    int i,y;fa[x][0]=f;
    for(i=1;i<=S;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
    for(i=LA[x];i;i=NE[i])
    {
        DFS(EN[i],x);
        Seg::rt[x]=Seg::Merge(Seg::rt[x],Seg::rt[EN[i]],1,m);
    }
}
int Find(int x,int le)
{
    for(int i=S;i>=0;i--)if(Max[fa[x][i]]>=le)x=fa[x][i];
    return x;
}
void Query(int l,int r,int x,int y)
{
    int p=Find(en[y],y-x+1);
    par t=Seg::GS(Seg::rt[p],1,m,l,r);
    printf("%d %d\n",t.second,t.first);
}
int main()
{
    int i,j,k,L,l,r,x,y;
    scanf("%s\n%d",s,&m);
    L=strlen(s);
    for(i=0;i<L;i++)Ins(s[i]-'a',0),en[i]=las;
    for(i=1;i<=m;i++)
    {
        scanf("\n%s",s);
        L=strlen(s);Ins(26,0);
        for(j=0;j<L;j++)Ins(s[j]-'a',i);
    }
    for(i=1;i<=tot;i++)ADD(pra[i],i);
    DFS(1,0);
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d%d%d%d",&l,&r,&x,&y);
        Query(l,r,x-1,y-1);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章