bzoj1396识别子串(SAM+线段树)

复习SAM板子啦!考前刷水有益身心健康当然这不是板子题/水题……

很容易发现只在i位置出现的串一定是个前缀串。那么对答案的贡献分成两部分:一部分是len[x]-fa~len[x]的这部分贡献会是r-l+1;剩下一部分1~len-fa-1这部分会和i~r构成答案,写两棵线段树即可。

然后就又是板子题了,两个板子(SAM+线段树)套起来。

#include<bits/stdc++.h>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int N=2e5+7,inf=0x3f3f3f3f;
int n,lst=1,rt=1,cnt=1,ch[N][26],len[N],fa[N],sz[N];
char s[N];
struct tree{
    int mn[N<<2];
    void init(){memset(mn,0x3f,sizeof mn);}
    void pushdown(int rt)
    {
        if(mn[rt]==inf)return;
        mn[rt<<1]=min(mn[rt<<1],mn[rt]),mn[rt<<1|1]=min(mn[rt<<1|1],mn[rt]);
        mn[rt]=inf;
    }
    void update(int L,int R,int v,int l,int r,int rt)
    {
        if(L<=l&&r<=R){mn[rt]=min(mn[rt],v);return;}
        int mid=l+r>>1;pushdown(rt);
        if(L<=mid)update(L,R,v,lson);
        if(R>mid)update(L,R,v,rson);
    }
    int query(int k,int l,int r,int rt)
    {
        if(l==r)return mn[rt];
        int mid=l+r>>1;pushdown(rt);
        if(k<=mid)return query(k,lson);
        return query(k,rson);
    }
}tr1,tr2;
void build(int c)
{
    int p=lst,np=++cnt;
    len[np]=len[p]+1,sz[np]=1;
    while(p&&!ch[p][c])ch[p][c]=np,p=fa[p];
    if(!p)fa[np]=rt;
    else{
        int q=ch[p][c];
        if(len[p]+1==len[q])fa[np]=q;
        else{
            int nq=++cnt;
            memcpy(ch[nq],ch[q],sizeof ch[q]);
            fa[nq]=fa[q],fa[np]=fa[q]=nq,len[nq]=len[p]+1;
            while(p&&ch[p][c]==q)ch[p][c]=nq,p=fa[p];
        }
    }
    lst=np;
}
int main()
{
    scanf("%s",s+1),n=strlen(s+1);
    for(int i=1;i<=n;i++)build(s[i]-'a');
    for(int i=1;i<=cnt;i++)sz[i]=1;
    for(int i=1;i<=cnt;i++)sz[fa[i]]=0;
    tr1.init(),tr2.init();
    for(int i=1;i<=cnt;i++)
    if(sz[i]==1)
    {
        int l=len[i]-len[fa[i]],r=len[i];
        if(l>=2)tr1.update(1,l-1,r+1,1,n,1);
        tr2.update(l,r,r-l+1,1,n,1);
    }
    for(int i=1;i<=n;i++)printf("%d\n",min(tr1.query(i,1,n,1)-i,tr2.query(i,1,n,1)));
}
View Code

 

posted @ 2019-05-21 23:07  hfctf0210  阅读(376)  评论(0编辑  收藏  举报