【JZOJ B组】【GDOI2016模拟】识别子串

Description

现在同学们把大多数作业都做完了,但是却被最后一个题给难住了。
一般地,对于一个字符串S,和S中第k个字符,定义子串T=S(i..j)为一个关于k的识别子串,当且仅当
1、i<=k<=j。
2、T在S中只出现一次。
比如,对于banana的第5个字符,“nana”,“anan”,“anana”,“nan”,“banan”和“banana”都是关于它的识别子串。
自然,识别子串越短越好(太长了也就失去意义了),现在请你计算出对于一个字符串S,关于S的每一位的最短识别子串分别有多长。

Input

一行,一个长度为L的字符串S,S只包含小写字母。

Output

L行,每行1个整数,第i行的数表示关于S的第i个元素的最短识别子串有多长。

Sample Input

agoodcookcooksgoodfood

Sample Output

1
2
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4

Data Constraint

第一个点 L=100
第二个点 L=1000
第三个点 L=5000
第四个点到第十个点 L=100000

思路

首先算出后缀数组,然后算那个 height 数组。
现在定义 extend[i] = i + max(height[rank[i]], height[rank[i]+1]),表示 extend[i]是从 i 开始的识别子串至少要延伸到哪里,即当且仅当 j>=extend[i]时,字串 T=S(i..j)在 S 中只出现一次。
所以,对于一个目标位置 k 和识别子串的起始位置 i,识别子串的终止位置 j 为max(extend[i],k)。
现在,我们用枚举识别子串的起始位置 i 去更新所有答案,下面分情况讨论。

  1. 当 i<=k<=extend[i]时,识别子串必须要延伸到 extend[i],用 extend[i]-i+1 去更新 ans[k]。这一步可以用线段树去更新 i..extend[i]这一段的 ans。
  2. 当 k>extend[i]时,识别子串必须要延伸到 k,用 k-i+1 去更新 ans[k]。注意到对于每个 k,显然 i 越大越好,所以可以倒着枚举 i 去更新 extend[i]以后还未被更新的 k。

时间复杂度 O(nlogn)
空间复杂度 O(n)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=100077*3,inf=0x3f3f3f3f;
int b[maxn],c[maxn],d[maxn],s[maxn],rank[maxn*2],height[maxn],SA[maxn],mn,wz,n;
char st[maxn*5];
struct E
{
    int left,right;
}e[maxn];
void get_SA(int n,int m)
{
    for (int i=1;i<=n;i++) b[s[i]]++;
    for (int i=1;i<=m;i++) b[i]+=b[i-1];
    for (int i=n;i>=1;i--) c[b[s[i]]--]=i;
    int x=0,j=1;
    for (int i=1;i<=n;i++)
    {
        if (s[c[i]]!=s[c[i-1]]) x++;
        rank[c[i]]=x;
    }
    while (j<=n)
    {
        for (int i=1;i<=n;i++) b[i]=0;
        for (int i=1;i<=n;i++) b[rank[i+j]]++;      
        for (int i=1;i<=n;i++) b[i]+=b[i-1];
        for (int i=n;i>=1;i--) c[b[rank[i+j]]--]=i;
        for (int i=1;i<=n;i++) b[i]=0;
        for (int i=1;i<=n;i++) b[rank[i]]++;
        for (int i=1;i<=n;i++) b[i]+=b[i-1];
        for (int i=n;i>=1;i--) d[b[rank[c[i]]]--]=c[i];
        x=0;
        for (int i=1;i<=n;i++)
        {
            if (rank[d[i]]!=rank[d[i-1]]||rank[d[i]]==rank[d[i-1]]&&rank[d[i]+j]!=rank[d[i-1]+j]) x++;
            c[d[i]]=x;
        }
        for (int i=1;i<=n;i++) rank[i]=c[i];
        if (x==n) break; 
        j=j*2;
    }
}
void get_height(int n)
{
    int x=0;
    for (int i=1;i<=n;i++) SA[rank[i]]=i;
    for (int i=1;i<=n;i++)
    {
        if (x!=0) x--;
        int j=SA[rank[i]-1];
        while (i+x<=n&&j+x<=n&&s[i+x]==s[j+x]) x++;
        height[rank[i]]=x;
    }
    height[1]=0;
}
void build(int x,int l,int r)
{
    e[x].left=-inf; e[x].right=inf;
    if (l==r) return;
    int mid=(l+r)/2;
    build(x*2,l,mid); build(x*2+1,mid+1,r);
}
void add1(int id,int l,int r,int x,int y,int z)
{
    if (l==x&&r==y) { e[id].right=min(e[id].right,z); return; }
    int mid=(l+r)/2;
    if (y<=mid) add1(id*2,l,mid,x,y,z);
    else if (x>mid) add1(id*2+1,mid+1,r,x,y,z);
         else add1(id*2,l,mid,x,mid,z),add1(id*2+1,mid+1,r,mid+1,y,z);
}
void add2(int id,int l,int r,int x,int y,int z)
{
    if (l==x&&r==y) { e[id].left=max(e[id].left,z); return; }
    int mid=(l+r)/2;
    if (y<=mid) add2(id*2,l,mid,x,y,z);
    else if (x>mid) add2(id*2+1,mid+1,r,x,y,z);
         else add2(id*2,l,mid,x,mid,z),add2(id*2+1,mid+1,r,mid+1,y,z);
}
void query(int id,int l,int r,int x)
{
    mn=min(mn,e[id].right); wz=max(wz,e[id].left);
    if (l==r) return;
    int mid=(l+r)/2;
    if (x<=mid) query(id*2,l,mid,x);
    else query(id*2+1,mid+1,r,x);
}
int main()
{
    scanf("%s",st+1); n=strlen(st+1);
    for(int i=1; i<=n; i++) s[i]=st[i]-96;
    get_SA(n,30); 
    get_height(n);
    build(1,1,n);
    for (int i=1;i<=n;i++)
    {
        int x=SA[i],l=max(height[i],height[i+1]);
        if (l==n-x+1) continue;
        add1(1,1,n,x,x+l,l+1);
        if (x+l+1<=n) add2(1,1,n,x+l+1,n,x);
    }
    for (int i=1;i<=n;i++)
    {
        mn=inf; wz=-inf; 
        query(1,1,n,i);
        printf("%d\n",min(mn,i-wz+1));
    }
}

猜你喜欢

转载自blog.csdn.net/eric1561759334/article/details/81045056