bzoj1396 识别子串【解法一】

来源:互联网 发布:java 汉字验证码 编辑:程序博客网 时间:2024/06/07 00:14

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

考虑对于一个位置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));}