Codeforces 666E Forensic Examination SAM or SA+线段树合并

E. Forensic Examination

http://codeforces.com/problemset/problem/666/E

题目大意:给模式串S以及m个特殊串,q个询问,询问S的子串[pl,pr]在特殊串编号属于[l,r]中出现次数最多的次数以及在哪个特殊串。

一开始打算用S建SAM,特殊串去匹配。。。测样例时才想起这样不对。胡搞一番后,才开始写下面的做法。

S与特殊串连在一起建SAM,记录S串[1,x]在SAM上节点位置,用来子串定位。每个节点的{right}该串出现的所有地方。于是题目成了求一个节点的{right}中属于某个特殊串次数最多的特殊串。给这些{right}染色,即标记属于哪个特殊串。一个节点用一棵线段树维护最值,然后用线段树合并求出每个节点的线段树。

复杂度O(nlogn)。。。这n大概为106 

#include<cstdio>
#include<cmath>
#include<cstring>
const int len(500000),lem(35000000),N(600000);
struct Node{int pre,nx[27],step;}sam[N*2+10];
int top=1,now=1,root=1,last,lastson;int n,pos[len+10];
int m,q,leng,l,r,pl,pr;char str[len+10];
int f[22][N*2+10],logg;
struct SegMent{int nx[2];unsigned short int big,pos;}tree[lem+10];
int stot,rt[N*2+10];
struct data{unsigned short int x,y;};
void extend(int x)
{
    last=now;now=++top;sam[now].step=sam[last].step+1;
    for(;!sam[last].nx[x]&&last;last=sam[last].pre)
        sam[last].nx[x]=now;
    if(!last)sam[now].pre=root;
    else
    {
        lastson=sam[last].nx[x];
        if(sam[lastson].step==sam[last].step+1)sam[now].pre=lastson;
        else
        {
            sam[++top]=sam[lastson];sam[top].step=sam[last].step+1;
            sam[now].pre=sam[lastson].pre=top;
            for(;sam[last].nx[x]==lastson&&last;last=sam[last].pre)
                sam[last].nx[x]=top;
        }
    }
}
void update(int k)
{
    int next;
    if(tree[tree[k].nx[0]].big>tree[tree[k].nx[1]].big||
    (tree[tree[k].nx[0]].big==tree[tree[k].nx[1]].big&&
    tree[tree[k].nx[0]].pos<tree[tree[k].nx[1]].pos))next=0;
    else next=1;
    tree[k].big=tree[tree[k].nx[next]].big;
    tree[k].pos=tree[tree[k].nx[next]].pos;
}
void insert(int &k,int l,int r,int x)
{
    if(!k)k=++stot;
    if(l==r){tree[k].big++;tree[k].pos=x;return;}
    int mid=(l+r)>>1;
    if(x<=mid)insert(tree[k].nx[0],l,mid,x);
    else insert(tree[k].nx[1],mid+1,r,x);
    update(k);
}
int find(int l,int r)
{
    int x=pos[r];
    for(int j=logg;j>=0;j--)
    if(sam[f[j][x]].step>=r-l+1)x=f[j][x];
    return x;
}
int merge(int x,int y,int l,int r)
{
    if(!x||!y)return x|y;
    int z=++stot;
    if(l==r){tree[z].big=tree[x].big+tree[y].big;tree[z].pos=tree[x].pos;return z;}
    int mid=(l+r)>>1;
    tree[z].nx[0]=merge(tree[x].nx[0],tree[y].nx[0],l,mid);
    tree[z].nx[1]=merge(tree[x].nx[1],tree[y].nx[1],mid+1,r);
    update(z);
    return z;
}
int cnt[N*2+10],p[N*2+10];
void update()
{
    for(int i=1;i<=top;i++)cnt[sam[i].step]++;
    for(int i=1;i<=top;i++)cnt[i]+=cnt[i-1];
    for(int i=top;i>=1;i--)p[cnt[sam[i].step]--]=i;
    for(int i=top;i>=1;i--)
        rt[sam[p[i]].pre]=merge(rt[sam[p[i]].pre],rt[p[i]],1,m);
}
void deal()
{
    logg=log(top)/log(2);
    for(int i=1;i<=top;i++)f[0][i]=sam[i].pre;
    for(int j=1;j<=logg;j++)
    for(int i=1;i<=top;i++)
    f[j][i]=f[j-1][f[j-1][i]];
}
data query(int k,int l,int r,int L,int R)
{
    if(!k)return (data){0,l};
    if(L==l&&R==r)return (data){tree[k].big,tree[k].pos};
    int mid=(L+R)>>1;
    if(r<=mid)return query(tree[k].nx[0],l,r,L,mid);
    else if(mid<l)return query(tree[k].nx[1],l,r,mid+1,R);
    else
    {
        data t1=query(tree[k].nx[0],l,mid,L,mid),t2=query(tree[k].nx[1],mid+1,r,mid+1,R);
        if(t1.x>t2.x||(t1.x==t2.x&&t1.y<t2.y))return t1;
        else return t2;
    }
}
void read(int &x)
{
    x=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
}
int main()
{
    freopen("C.in","r",stdin);
    freopen("C2.out","w",stdout);
    scanf("%s",str);
    n=strlen(str);
    for(int i=0;i<n;i++)extend(str[i]-'a'),pos[i+1]=now;
    extend(26);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",str);leng=strlen(str);
        for(int j=0;j<leng;j++)extend(str[j]-'a'),insert(rt[now],1,m,i);
        extend(26);
    }
    deal();update();
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d%d%d",&l,&r,&pl,&pr);
        int x=find(pl,pr);data tmp;
        tmp=query(rt[x],l,r,1,m);
        if(tmp.x==0)tmp.y=l;
        printf("%d %d\n",tmp.y,tmp.x);
    }
    return 0;
}

 

 

第二种写法:

将串连在一起求SA,设L为小于rank[pl]的第一个height[L]=pr-pl+1,R为大于rank[pl]的第一个height[R]=pr-pl+1。那么[pl,pr]能匹配的后缀区间即[L,R]。给后缀标记一下属于哪个特殊串,将题目变成了求区间众数。有趣的是这些区间不是包含关系就是相离关系。
令height[i]=(i-1,i)的(无向)边权,那么一次询问就是询问点rank[pl]只经过边权大于等于(pr-pl+1)的边所能到达点中出现次数最多的特殊串。
于是变成B3545Peaks加强版,用线段树维护,线段树合并实现。

O(nlogn)

#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
const int limt(28),len(600000),lem(40000000);
int tmp[len+10],cnt[len+10],p[len+10];
int rank[len*2+10],sfa[len+10],height[len+10];
int str[len+10],color[len+10],val[len*2+10];
int n,m,Q,leng,l,r,pl,pr,x;char ch[len+10];
struct Node{int nd,nx,co;}bot[len*2+10];int tot,first[len*2+10];
int g[21][len*2+10],f[21][len*2+10],logg;
int father[len*2+10],now,last;
std::vector<int>weight[len+10];
struct ANS
{
    unsigned short int big,pos;
inline ANS operator +(const ANS&A)const{return (ANS){big+A.big,pos};}
inline ANS operator *(const ANS&A)const{if(A.big<big||(big==A.big&&pos<A.pos))return (ANS){big,pos};return A;}
}ans;
struct SegMent{int nx[2];ANS key;}tree[lem+10];int stot,root[len*2+10];
void read(int &x)
{
    x=0;int f=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&(ch!='-')){ch=getchar();}if(ch=='-')f=-1,ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    if(f<0)x=-x;
}
void add(int a,int b,int c){bot[++tot]=(Node){b,first[a],c};first[a]=tot;}
void swap(int &a,int &b){if(a==b)return;a^=b;b^=a;a^=b;}
int min(int a,int b){return a>b?b:a;}

bool com(int x,int y,int l){return rank[x]==rank[y]&&rank[x+l]==rank[y+l];}
void doubling()
{
    for(int i=1;i<=n;i++)rank[i]=str[i],sfa[i]=i;
    for(int pos=0,l=0,sigma=limt;pos<n;sigma=pos)
    {
        pos=0;
        for(int i=n-l+1;i<=n;i++)p[++pos]=i;
        for(int i=1;i<=n;i++)if(sfa[i]>l)p[++pos]=sfa[i]-l;
        memset(cnt,0,sizeof(int)*(sigma+1));pos=0;
        for(int i=1;i<=n;i++)cnt[rank[i]]++;
        for(int i=1;i<=sigma;i++)cnt[i]+=cnt[i-1];
        for(int i=n;i>=1;i--)sfa[cnt[rank[p[i]]]--]=p[i];
        for(int i=1;i<=n;i++)tmp[sfa[i]]=com(sfa[i],sfa[i-1],l)?pos:++pos;
        for(int i=1;i<=n;i++)rank[i]=tmp[i];
        l=!l?1:l<<1;
    }
    for(int i=1;i<=n;i++)rank[sfa[i]]=i;
    for(int i=1,j,k;i<=n;i++)
    {
        k=sfa[rank[i]-1]; if(!k)continue;
        j=height[rank[i-1]]; if(j)j--;
        while(str[i+j]==str[k+j])
            j++;
        height[rank[i]]=j;
    }
}
int gf(int x)
{
    int v=x,o;while(v!=father[v])v=father[v];
    for(;x!=father[x];x=o){o=father[x];father[x]=v;}
    return v;
}
void exchange()
{
    for(int i=2;i<=n;i++)weight[height[i]].push_back(i);
    for(int i=1;i<=n;i++)val[i]=color[sfa[i]];
    for(int i=1;i<=n*2;i++)father[i]=i; now=n;last=now;
    for(int i=n,fa,fb;i>=1;i--)
    {
        last=now;
        for(int v=0;v<(int)weight[i].size();v++)
        {
            int t=weight[i][v];
            fa=gf(weight[i][v]-1);fb=gf(weight[i][v]);
            if(fa!=fb)
            {
                now++;
                father[fb]=father[fa]=father[now];
                if(now!=fb)add(now,fb,i);
                if(now!=fa)add(now,fa,i);
            }
        }
    }
    now++;
    for(int i=1,fa;i<=n;i++)
    {
        fa=gf(i);
        if(fa!=now)add(now,fa,0);father[fa]=now;
    }
}
void update(int k){tree[k].key=tree[tree[k].nx[0]].key*tree[tree[k].nx[1]].key;}
void insert(int &k,int l,int r,int x)
{
    if(!k)k=++stot;
    if(l==r){tree[k].key.big++;tree[k].key.pos=l;return;}
    int mid=(l+r)>>1;
    if(x<=mid)insert(tree[k].nx[0],l,mid,x);
    else insert(tree[k].nx[1],mid+1,r,x);
    update(k);
}
int merge(int x,int y,int l,int r)
{
    if(!x||!y)return (x|y);
    int z=++stot,mid=(l+r)>>1;
    if(l==r){tree[z].key=tree[x].key+tree[y].key;return z;}
    tree[z].nx[0]=merge(tree[x].nx[0],tree[y].nx[0],l,mid);
    tree[z].nx[1]=merge(tree[x].nx[1],tree[y].nx[1],mid+1,r);
    update(z);
    return z;
}
void dfs(int x)
{
    if(val[x])insert(root[x],1,m,val[x]);
    for(int v=first[x];v;v=bot[v].nx)
    {
        dfs(bot[v].nd); f[0][bot[v].nd]=x; g[0][bot[v].nd]=bot[v].co;
        root[x]=merge(root[x],root[bot[v].nd],1,m);
//        fprintf(stdout,"%d\n",stot);
    }
}
void Fdeal()
{
    logg=log(now)/log(2);
    for(int j=1;j<=logg;j++)
    for(int i=1;i<=now;i++)
     f[j][i]=f[j-1][f[j-1][i]],g[j][i]=min(g[j-1][i],g[j-1][f[j-1][i]]);
}
int find(int pl,int pr)
{
    int x=rank[pl];
    for(int j=logg;j>=0;j--)
    if(g[j][x]>=pr-pl+1)x=f[j][x];
    return x;
}
ANS query(int k,int L,int R,int l,int r)
{
    if(!k)return (ANS){0,l};
    if(l==L&&r==R)return tree[k].key;
    int mid=(L+R)>>1;
    if(r<=mid)return query(tree[k].nx[0],L,mid,l,r);
    else if(mid<l)return query(tree[k].nx[1],mid+1,R,l,r);
    else return query(tree[k].nx[0],L,mid,l,mid)*query(tree[k].nx[1],mid+1,R,mid+1,r);
}
int main()
{
//    freopen("C.in","r",stdin);
//    freopen("C.out","w",stdout);
    scanf("%s",ch);
    leng=strlen(ch);
    for(int i=0;i<leng;i++)str[++n]=ch[i]-'a'+1;
    str[++n]=27; scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",ch);
        leng=strlen(ch);
        for(int j=0;j<leng;j++)str[++n]=ch[j]-'a'+1,color[n]=i;
        str[++n]=27;
    }
    doubling();
    exchange();//将序列变成树
    dfs(now);
    Fdeal();
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++)
    {
        read(l);read(r);read(pl);read(pr);
        x=find(pl,pr);
        ans=query(root[x],1,m,l,r);
        printf("%d %d\n",ans.pos,ans.big);
    }
    return 0;
}

 

posted @ 2017-03-24 15:58  Oncle_Ha  阅读(626)  评论(0编辑  收藏  举报