BZOJ 1396: 识别子串(SAM+线段树)

题意:

题解:

SAM+线段树

先建出SAM,根据题意,只有 e n d p o s endpos endpos集合大小为 1 1 1的结点才能造成贡献。
在这里插入图片描述
对于每一个 e n d p o s endpos endpos集合大小为 1 1 1的我们都能算出 m a x l e n maxlen maxlen m i n l e n minlen minlen,显然 Ⅱ Ⅱ 区域内每个点的贡献就是 Ⅱ Ⅱ 区域的长度, Ⅰ Ⅰ 区域内每个点的贡献就是到 Ⅱ Ⅱ 区域的距离加上 Ⅱ Ⅱ 区域的长度。
建两棵线段树即可,一个修改 Ⅰ Ⅰ 区域,一个修改 Ⅱ Ⅱ 区域。

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5+50;
const int INF = 0x3f3f3f3f;
struct Tree{
    struct Seg{
        int l,r,mi,lazy;
    }seg[MAXN<<2];
    inline void pushup(int rt){ seg[rt].mi=min(seg[rt<<1].mi,seg[rt<<1|1].mi); }
    inline void pushdown(int rt){
        if(seg[rt].lazy<INF){
            seg[rt<<1].mi=min(seg[rt<<1].mi,seg[rt].lazy);
            seg[rt<<1].lazy=min(seg[rt<<1].lazy,seg[rt].lazy);
            seg[rt<<1|1].mi=min(seg[rt<<1|1].mi,seg[rt].lazy);
            seg[rt<<1|1].lazy=min(seg[rt<<1|1].lazy,seg[rt].lazy);
            seg[rt].lazy=INF;
        }
    }
    inline void Build(int rt,int l,int r){
        seg[rt].mi=seg[rt].lazy=INF;
        seg[rt].l=l,seg[rt].r=r;
        if(l==r) return;
        int mid = (l+r)>>1;
        Build(rt<<1,l,mid);
        Build(rt<<1|1,mid+1,r);
    }
    inline void update(int rt,int l,int r,int val){
        if(seg[rt].r<l || seg[rt].l>r || l>r) return;
        if(l<=seg[rt].l && seg[rt].r<=r){
            seg[rt].mi = min(seg[rt].mi,val);
            seg[rt].lazy = min(seg[rt].lazy,val);
            return;
        }
        int mid = (seg[rt].l+seg[rt].r)>>1;
        pushdown(rt);
        if(l<=mid) update(rt<<1,l,r,val);
        if(r>mid) update(rt<<1|1,l,r,val);
        pushup(rt);
    }
}T1,T2;
int ans[MAXN];
char s[MAXN];
int pos[MAXN],a[MAXN],c[MAXN];
int nxt[MAXN][26],fa[MAXN],len[MAXN];
int endpos[MAXN];
int last=1,tot=1,n;
void Query(int rt,int l,int r){
    if(l==r){
        ans[l] = min(T1.seg[rt].mi-l,T2.seg[rt].mi);
        return;
    }
    int mid = (l+r)>>1;
    T1.pushdown(rt);
    T2.pushdown(rt);
    Query(rt<<1,l,mid);
    Query(rt<<1|1,mid+1,r);
}
inline void Insert(int x,int k){
    int p=last,np=++tot;
    last=np,len[np]=len[p]+1;
    for(;p&&!nxt[p][x];p=fa[p]) nxt[p][x]=np;
    if(!p) fa[np]=1;
    else{
        int q=nxt[p][x];
        if(len[p]+1==len[q]) fa[np]=q;
        else{
            int nq=++tot;
            len[nq]=len[p]+1;
            memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
            fa[nq]=fa[q];
            fa[q]=fa[np]=nq;
            for(;nxt[p][x]==q;p=fa[p]) nxt[p][x]=nq;
        }
    }
    pos[np]=k;
    endpos[np]=1;
}
inline void Sort(){
    for(int i=1;i<=tot;i++) ++c[len[i]];
    for(int i=1;i<=tot;i++) c[i]+=c[i-1];
    for(int i=1;i<=tot;i++) a[c[len[i]]--]=i;
}
int main(){
    scanf("%s",s+1);
    n = strlen(s+1);
    for(int i=1;i<=n;i++) Insert(s[i]-'a',i);
    Sort();
    T1.Build(1,1,n); T2.Build(1,1,n);
    for(int i=tot;i;i--){
        int x = a[i];
        endpos[fa[x]] += endpos[x];
        if(x==1 || endpos[x]>1) continue;
        int l = pos[x]-len[x]+1;
        int r = pos[x]-(len[fa[x]]+1)+1;
        T1.update(1,l,r-1,pos[x]+1);
        T2.update(1,r,pos[x],pos[x]-r+1);
    }
    Query(1,1,n);
    for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值