bzoj1396 识别子串【解法一】

解法一【后缀数组+单调队列】见【这里】

考虑对于一个位置 i ,它至少要延伸p=max(height[rank[i]],height[rank[i]+1])的长度才能成为识别子串。因此它对答案的贡献分为两部分,一部分是 i..i+p ,这一部分的答案都是 p+1 ,再往后到结束的部分是一个斜率为 1 的线段,这两种修改都可以用线段树维护。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000010,oo=0x3f3f3f3f;
char s[maxn];
int sa[maxn],rank[maxn],height[maxn],f[maxn],cnt[maxn],
tag1[maxn],tag2[maxn],val[maxn],
n;
void init(int p,int L,int R)
{
    tag1[p]=tag2[p]=oo;
    if (L<R)
    {
        int mid=L+R>>1;
        init(p<<1,L,mid);
        init(p<<1|1,mid+1,R);
    }
}
void modi1(int p,int L,int R,int l,int r,int x)
{
    if (l<=L&&R<=r) tag1[p]=min(tag1[p],x);
    else
    {
        int mid=L+R>>1;
        if (l<=mid) modi1(p<<1,L,mid,l,r,x);
        if (r>mid) modi1(p<<1|1,mid+1,R,l,r,x);
    }
}
void modi2(int p,int L,int R,int l,int r,int x)
{
    if (l<=L&&R<=r) tag2[p]=min(tag2[p],x);
    else
    {
        int mid=L+R>>1;
        if (l<=mid) modi2(p<<1,L,mid,l,r,x);
        if (r>mid) modi2(p<<1|1,mid+1,R,l,r,x);
    }
}
int get(int p,int L,int R,int x)
{
    int ret=min(tag1[p],x+tag2[p]);
    if (L<R)
    {
        int mid=L+R>>1;
        if (x<=mid) ret=min(ret,get(p<<1,L,mid,x));
        else ret=min(ret,get(p<<1|1,mid+1,R,x));
    }
    return ret;
}
int main()
{
    int m=26,p;
    scanf("%s",s+1);
    n=strlen(s+1);
    for (int i=1;i<=n;i++) cnt[rank[i]=s[i]-'a'+1]++;
    for (int i=2;i<=m;i++) cnt[i]+=cnt[i-1];
    for (int i=n;i;i--) sa[cnt[rank[i]]--]=i;
    for (int k=1;;k<<=1)
    {
        p=0;
        for (int i=n-k+1;i<=n;i++) f[++p]=i;
        for (int i=1;i<=n;i++)
            if (sa[i]>k) f[++p]=sa[i]-k;
        for (int i=1;i<=m;i++) cnt[i]=0;
        for (int i=1;i<=n;i++) cnt[rank[f[i]]]++;
        for (int i=2;i<=m;i++) cnt[i]+=cnt[i-1];
        for (int i=n;i;i--) sa[cnt[rank[f[i]]]--]=f[i];
        for (int i=1;i<=n;i++) f[i]=rank[i];
        rank[sa[1]]=1;
        for (int i=2;i<=n;i++)
            if (f[sa[i]]==f[sa[i-1]]&&f[sa[i]+k]==f[sa[i-1]+k])
                rank[sa[i]]=rank[sa[i-1]];
            else rank[sa[i]]=rank[sa[i-1]]+1;
        m=rank[sa[n]];
        if (m>=n) break;
    }
    for (int i=1;i<=n;i++)
    {
        height[rank[i]]=height[rank[i-1]];
        if (height[rank[i]]) height[rank[i]]--;
        while (s[i+height[rank[i]]]==s[sa[rank[i]-1]+height[rank[i]]]) height[rank[i]]++;
    }
    init(1,1,n);
    for (int i=1;i<=n;i++)
    {
        p=max(height[rank[i]],height[rank[i]+1]);
        if (i+p<=n)
        {
            modi1(1,1,n,i,i+p,p+1);
            modi2(1,1,n,i+p,n,-i+1);
        }
    }
    for (int i=1;i<=n;i++) printf("%d\n",get(1,1,n,i));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值