CF666E Forensic Examination(广义后缀自动机+线段树合并)

Posted rikurika

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF666E Forensic Examination(广义后缀自动机+线段树合并)相关的知识,希望对你有一定的参考价值。

Luogu

给你一个串 $ S $ 以及一个字符串数组 $ T_1 ~ T_m $ , $ q $ 次询问,每次问 $ S $ 的子串S[p_l,p_r]在 $ T_l ~ T_r $ 中的哪个串里的出现次数最多,并输出出现次数。

如有多解输出最靠前的那一个。

题解时间

SAM的毒瘤题,无论是倍增来满足长度限制,线段树合并来求区间询问,应有尽有。。。

对于 $ T $ 串建广义SAM,之后考虑如何使得 $ S $ 在SAM上匹配时求出 $ S $ 在每个 $ T $ 的出现次数。

很明显用线段树合并就可以搞:

每插入 $ T_i $ 的一个字符就在 $ fin $ 上挂上一个 $ i $ 。

然后跳parent树搞线段树合并就好。

之后,由于每次 $ S $ 也只选中一部分,所以考虑:

对于一组询问 $ [l,r,pl,pr] $ ,首先毫无疑问将询问挂在 $ S $ 的 $ pr $ 位上。

之后匹配到 $ pr $ 位时,倍增跳现所处点 $ px $ 的 $ pre $ 到最上面的 $ x $ 使得 $ len[x]>=(pr-pl+1) $ 。

然后线段树上求答案即可。

#include<bits/stdc++.h>
using namespace std;
namespace RKK
{
const int N=1000011;
int n,m,qaq;
char s0[N],s1[N];int l0,l1;
struct sumireko{int to,ne;}e[N];int he[N],ecnt;
void addline(int f,int t){e[++ecnt].to=t;e[ecnt].ne=he[f];he[f]=ecnt;}
int fa[N][22];
int tcnt,rt[N],lson[N<<3],rson[N<<3];
struct pat
{
    int first,second;
    bool operator < (const pat &a)const{return second==a.second?first>a.first:second<a.second;}
};
pat ma[N<<3],ans[N];
void fuckup(int px){ma[px]=max(ma[lson[px]],ma[rson[px]]);}
void insert(int x,int &px,int pl,int pr)
{
    if(!px) px=++tcnt;
    if(pl==pr)
    {
        ma[px].first=x,ma[px].second++;
        return;
    }
    int pm=pl+pr>>1;
    if(x<=pm) insert(x,lson[px],pl,pm);
    else insert(x,rson[px],pm+1,pr);
    fuckup(px);
}
int merge(int px,int py,int pl,int pr)
{
    if(!px||!py) return px|py;
    int pz=++tcnt;
    if(pl==pr){ma[pz]=(pat){pl,ma[px].second+ma[py].second};}
    else
    {
        int pm=pl+pr>>1;
        lson[pz]=merge(lson[px],lson[py],pl,pm);
        rson[pz]=merge(rson[px],rson[py],pm+1,pr);
        fuckup(pz);
    }
    return pz;
}
void query(int l,int r,int px,int pl,int pr,pat &pa)
{
    if(!px) return;
    if(l<=pl&&r>=pr){pa=max(pa,ma[px]);return;}
    int pm=pl+pr>>1;
    if(l<=pm) query(l,r,lson[px],pl,pm,pa);
    if(r>pm) query(l,r,rson[px],pm+1,pr,pa);
}
struct ques{int p,le,l,r,id;};
vector<ques> ve[N];
struct remilia{int tranc[26],len,pre;};
struct sakuya
{
    remilia s[N];
    int fin,size;
    sakuya(){fin=size=1;}
    void ins(int ch)
    {
        int npx,npy,lpx,lpy;
        if(s[fin].tranc[ch])
        {
            lpx=fin,lpy=s[fin].tranc[ch];
            if(s[lpy].len==s[lpx].len+1) fin=lpy;
            else
            {
                npy=++size;
                s[npy]=s[lpy];
                s[npy].len=s[lpx].len+1;
                s[lpy].pre=npy;
                while(s[lpx].tranc[ch]==lpy)
                {
                    s[lpx].tranc[ch]=npy;
                    lpx=s[lpx].pre;
                }
                fin=npy;
            }
            return;
        }
        npx=++size;
        s[npx].len=s[fin].len+1;
        for(lpx=fin;lpx&&!s[lpx].tranc[ch];lpx=s[lpx].pre) s[lpx].tranc[ch]=npx;
        if(!lpx) s[npx].pre=1;
        else
        {
            lpy=s[lpx].tranc[ch];
            if(s[lpy].len==s[lpx].len+1) s[npx].pre=lpy;
            else
            {
                npy=++size;
                s[npy]=s[lpy];
                s[npy].len=s[lpx].len+1;
                s[npx].pre=s[lpy].pre=npy;
                while(s[lpx].tranc[ch]==lpy)
                {
                    s[lpx].tranc[ch]=npy;
                    lpx=s[lpx].pre;
                }
            }
        }
        fin=npx;
    }
    void dfs(int x)
    {
        for(int i=he[x],t=e[i].to;i;i=e[i].ne,t=e[i].to)
        {
            dfs(t),rt[x]=merge(rt[x],rt[t],1,m);
        }
    }
    void work()
    {
        for(int i=2;i<=size;i++) fa[i][0]=s[i].pre,addline(s[i].pre,i);
        for(int k=1;k<=21;k++)for(int i=2;i<=size;i++) fa[i][k]=fa[fa[i][k-1]][k-1];
        dfs(1);
        int px=1,lnow=0;
        for(int i=1;i<=l0;i++)
        {
            while(px&&!s[px].tranc[s0[i]-'a']) px=s[px].pre,lnow=s[px].len;
            if(!px) px=1,lnow=0;
            else px=s[px].tranc[s0[i]-'a'],lnow++;
            for(int j=0;j<ve[i].size();j++)
            {
                ques &qn=ve[i][j];
                ans[qn.id].first=qn.l;
                if(lnow<qn.le){continue;}
                int x=px;
                for(int k=21;k>=0;k--)if(s[fa[x][k]].len>=qn.le) x=fa[x][k];
                query(qn.l,qn.r,rt[x],1,m,ans[qn.id]);
            }
        }
    }
}sam;
int Iris()
{
    scanf("%s",s0+1),l0=strlen(s0+1);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s1+1),l1=strlen(s1+1);
        sam.fin=1;
        for(int j=1;j<=l1;j++) sam.ins(s1[j]-'a'),insert(i,rt[sam.fin],1,m);
    }
    scanf("%d",&qaq);
    for(int i=1,l,r,x,y;i<=qaq;i++)
    {
        scanf("%d%d%d%d",&l,&r,&x,&y);
        ve[y].push_back((ques){y,y-x+1,l,r,i});
    }
    sam.work();
    for(int i=1;i<=qaq;i++)
        printf("%d %d
",ans[i].first,ans[i].second);
    return 0;
}
}
int main(){return RKK::Iris();}

以上是关于CF666E Forensic Examination(广义后缀自动机+线段树合并)的主要内容,如果未能解决你的问题,请参考以下文章

CF666E Forensic Examination

[CF666E]Forensic Examination

CF666E Forensic Examination [后缀自动机,线段树合并]

CF666E Forensic Examination SAM+倍增,线段树和并

CF666E Forensic Examination(后缀自动机+线段树合并)

CF666E Forensic Examination(广义后缀自动机+线段树合并)