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

我们建SAM,搞出parent树。
然后考虑哪些串是可能的识别子串,显然是那些|Right|=1的节点所代表的串。考虑这样的节点x,它的mn=mx[par[x]]+1,mx=mx[x],结尾字符为原串第r个。则他会对r-mn+1~r贡献一个mn的可能答案,对r-mx+1~r-mn贡献一个
-x+r+1的可能答案。于是两棵线段树分别维护一下最小值即可。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
int n=0,m,par[N<<1],son[N<<1][26],mx[N<<1],sz[N<<1],id[N<<1];
int a[N<<1],cnt[N],rt,lst;
char s[N];
struct node{
    int tag1,tag2;
}tr[N<<2];
inline void ins(int ch,int xx){
    int p=lst,np=++n;lst=np;mx[np]=mx[p]+1;sz[np]=1;id[np]=xx;
    for(;p&&!son[p][ch];p=par[p]) son[p][ch]=np;
    if(!p){par[np]=rt;return;}
    int q=son[p][ch];
    if(mx[q]==mx[p]+1){par[np]=q;return;}
    int nq=++n;mx[nq]=mx[p]+1;memcpy(son[nq],son[q],sizeof(son[q]));
    par[nq]=par[q];par[q]=par[np]=nq;
    for(;p&&son[p][ch]==q;p=par[p]) son[p][ch]=nq;
}
inline void change(int p,int l,int r,int x,int y,int val){
    if(x<=l&&r<=y){tr[p].tag1=min(tr[p].tag1,val);return;}
    int mid=l+r>>1;
    if(x<=mid) change(p<<1,l,mid,x,y,val);
    if(y>mid) change(p<<1|1,mid+1,r,x,y,val);
}
inline void change2(int p,int l,int r,int x,int y,int val){
    if(x<=l&&r<=y){tr[p].tag2=min(tr[p].tag2,val);return;}
    int mid=l+r>>1;
    if(x<=mid) change2(p<<1,l,mid,x,y,val);
    if(y>mid) change2(p<<1|1,mid+1,r,x,y,val);
}
inline int ask(int p,int l,int r,int x){
    if(l==r) return tr[p].tag1;
    int mid=l+r>>1;
    if(x<=mid) return min(ask(p<<1,l,mid,x),tr[p].tag1);
    return min(ask(p<<1|1,mid+1,r,x),tr[p].tag1);
}
inline int ask2(int p,int l,int r,int x){
    if(l==r) return tr[p].tag2;
    int mid=l+r>>1;
    if(x<=mid) return min(ask2(p<<1,l,mid,x),tr[p].tag2);
    return min(ask2(p<<1|1,mid+1,r,x),tr[p].tag2);
}
inline void build(int p,int l,int r){
    tr[p].tag1=tr[p].tag2=inf;
    if(l==r) return;int mid=l+r>>1;
    build(p<<1,l,mid);build(p<<1|1,mid+1,r);
}
int main(){
//  freopen("a.in","r",stdin);
    scanf("%s",s+1);m=strlen(s+1);lst=rt=++n;
    for(int i=1;i<=m;++i) ins(s[i]-'a',i);
    for(int i=1;i<=n;++i) cnt[mx[i]]++;
    for(int i=1;i<=m;++i) cnt[i]+=cnt[i-1];
    for(int i=n;i>=1;--i) a[cnt[mx[i]]--]=i;build(1,1,m);
    for(int i=n;i>=1;--i){
        int x=a[i];sz[par[x]]+=sz[x];if(sz[x]>1) continue;
        int len=mx[par[x]]+1,r=id[x];
        change(1,1,m,r-len+1,r,len);change2(1,1,m,r-mx[x]+1,r-len+1,r+1);
    }for(int i=1;i<=m;++i){
        int res=ask(1,1,m,i),res1=-i+ask2(1,1,m,i);
        printf("%d\n",min(res,res1));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Icefox_zhx/article/details/80723171