[BZOJ1396]识别子串(后缀自动机+线段树)

70 篇文章 0 订阅
18 篇文章 0 订阅

题目描述

传送门

题解

这题思路还是比较好想的
建立后缀自动机了之后统计每一个点right集合的大小,若一个点right集合的大小为1,那么以这个点为右端点的合法子串都是只在字符串中出现过一遍的,所谓合法就是长度在这个点的长度区间[Min,Max]里,这个点在原串中的位置也就是step
那么如何求每一个位置的最小值呢?
假设某一个点right集合大小为1,右端点为step,合法的长度区间为[Min,Max],那么显然[r-Min+1,r]中的点长度都可以取Min,而[r-Max+1,r-Min]中的点i长度都可以取Max-i+1,这就是一些区间修改的问题了
但是并不能用线段树直接维护,因为修改的东西不一样!但是可以发现[r-Min+1,r]和[r-Max+1,r-Min]可以分开修改,前者直接修改长度,后者修改向右边最近的点
就是要写两棵线段树了..
想了想好像sa也是可以写的,就是搞出来height了之后对于每一个和前后取一下min,然后线段树的处理方法和sam是差不多的,有时间写一写吧

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 200005

char s[N];
int n,root,sz,last,p,np,q,nq;
int ch[N][30],pre[N],step[N],c[N],pt[N],rt[N],loc[N];

void extend()
{
    root=last=++sz;
    for (int i=1;i<=n;++i)
    {
        int x=s[i]-'a';
        p=last;np=++sz;last=np;
        step[np]=step[p]+1;
        while (p&&!ch[p][x])
        {
            ch[p][x]=np;
            p=pre[p];
        }
        if (!p) pre[np]=root;
        else
        {
            q=ch[p][x];
            if (step[q]==step[p]+1) pre[np]=q;
            else
            {
                nq=++sz;
                step[nq]=step[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                pre[nq]=pre[q];
                while (ch[p][x]==q)
                {
                    ch[p][x]=nq;
                    p=pre[p];
                }
                pre[np]=pre[q]=nq;
            }
        }
    }
}
namespace Segtreeval
{
    int mn[N*4],delta[N*4],inf;
    void init()
    {
        memset(mn,127,sizeof(mn));
        memset(delta,127,sizeof(delta));
        inf=mn[0];
    }
    void update(int now)
    {
        mn[now]=min(mn[now<<1],mn[now<<1|1]);
    }
    void pushdown(int now)
    {
        if (delta[now]!=inf)
        {
            mn[now<<1]=min(mn[now<<1],delta[now]);
            delta[now<<1]=min(delta[now<<1],delta[now]);
            mn[now<<1|1]=min(mn[now<<1|1],delta[now]);
            delta[now<<1|1]=min(delta[now<<1|1],delta[now]);
            delta[now]=inf;
        }
    }
    void change(int now,int l,int r,int lr,int rr,int x)
    {
        if (lr>rr) return;
        int mid=(l+r)>>1;
        if (lr<=l&&r<=rr)
        {
            mn[now]=min(mn[now],x);
            delta[now]=min(delta[now],x);
            return;
        }
        pushdown(now);
        if (lr<=mid) change(now<<1,l,mid,lr,rr,x);
        if (mid+1<=rr) change(now<<1|1,mid+1,r,lr,rr,x);
        update(now);
    }
    int query(int now,int l,int r,int x)
    {
        int mid=(l+r)>>1;
        if (l==r) return mn[now];
        pushdown(now);
        if (x<=mid) return query(now<<1,l,mid,x);
        else return query(now<<1|1,mid+1,r,x);
    }
}
namespace Segtreeloc
{
    int mn[N*4],delta[N*4],inf;
    void init()
    {
        memset(mn,127,sizeof(mn));
        memset(delta,127,sizeof(delta));
        inf=mn[0];
    }
    void update(int now)
    {
        mn[now]=min(mn[now<<1],mn[now<<1|1]);
    }
    void pushdown(int now)
    {
        if (delta[now]!=inf)
        {
            mn[now<<1]=min(mn[now<<1],delta[now]);
            delta[now<<1]=min(delta[now<<1],delta[now]);
            mn[now<<1|1]=min(mn[now<<1|1],delta[now]);
            delta[now<<1|1]=min(delta[now<<1|1],delta[now]);
            delta[now]=inf;
        }
    }
    void change(int now,int l,int r,int lr,int rr,int x)
    {
        if (lr>rr) return;
        int mid=(l+r)>>1;
        if (lr<=l&&r<=rr)
        {
            mn[now]=min(mn[now],x);
            delta[now]=min(delta[now],x);
            return;
        }
        pushdown(now);
        if (lr<=mid) change(now<<1,l,mid,lr,rr,x);
        if (mid+1<=rr) change(now<<1|1,mid+1,r,lr,rr,x);
        update(now);
    }
    int query(int now,int l,int r,int x)
    {
        int mid=(l+r)>>1;
        if (l==r) return mn[now];
        pushdown(now);
        if (x<=mid) return query(now<<1,l,mid,x);
        else return query(now<<1|1,mid+1,r,x);
    }
}
int main()
{
    scanf("%s",s+1);n=strlen(s+1);
    extend();
    for (int i=1;i<=sz;++i) ++c[step[i]];
    for (int i=1;i<=sz;++i) c[i]+=c[i-1];
    for (int i=sz;i>=1;--i) pt[c[step[i]]--]=i;
    p=root;
    for (int i=1;i<=n;++i)
    {
        int x=s[i]-'a';
        p=ch[p][x];
        ++rt[p];loc[p]=step[p];
    }
    for (int i=sz;i>=1;--i)
    {
        p=pt[i];
        rt[pre[p]]+=rt[p],loc[pre[p]]=loc[p];
    }
    Segtreeval::init();Segtreeloc::init();
    for (int i=1;i<=sz;++i)
        if (rt[i]==1)
        {
            int a=loc[i];
            int b=a-step[pre[i]];
            Segtreeval::change(1,1,n,b,a,step[pre[i]]+1);
            int c=a-step[i]+1;
            Segtreeloc::change(1,1,n,c,b-1,loc[i]);
        }
    for (int i=1;i<=n;++i)
    {
        int ans=min(Segtreeval::query(1,1,n,i),Segtreeloc::query(1,1,n,i)-i+1);
        printf("%d\n",ans);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值