[后缀数组][线段树] Jzoj P4372 识别子串

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

 

 

题解

  • 后缀数组可以求出以第i位开头的最短的在原串中只出现过一次的子串

  • 子串的长度是min(height[rank[i]], height[rank[i] + 1) + 1
  • 所以我们枚举每个位置i,找到这个串,然后考虑它的贡献:
  • 对于这个串之内的位置,答案可以用这个串的长度更新;

     

  • 对于这个串右边的位置,串可以向右“延伸”直到包含该位置(延伸后的串显然也只出现过一次),所以答案可以用(该位置 - i + 1)来更新

     

  • 可以用线段树维护即可

  • 如果有后缀数组不熟的同学,详见:链接

代码

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<algorithm>
  5 using namespace std;
  6 const int N=500005,inf=1000000000;
  7 int n,s[N],b[N],c[N],d[N],rank[N*2],sa[N],height[N],wz,mn;
  8 char str[N];
  9 struct edge{int v,p;}e[N*4];
 10 void get_SA(int n,int m)
 11 {
 12     for (int i=1;i<=n;i++) b[s[i]]++;
 13     for (int i=1;i<=m;i++) b[i]+=b[i-1];
 14     for (int i=n;i>=1;i--) c[b[s[i]]--]=i;
 15     int x=0,j=1;
 16     for (int i=1;i<=n;i++)
 17     {
 18         if (s[c[i]]!=s[c[i-1]]) x++;
 19         rank[c[i]]=x;
 20     }
 21     while (j<=n)
 22     {
 23         for (int i=1;i<=n;i++) b[i]=0;
 24         for (int i=1;i<=n;i++) b[rank[i+j]]++;        
 25         for (int i=1;i<=n;i++) b[i]+=b[i-1];
 26         for (int i=n;i>=1;i--) c[b[rank[i+j]]--]=i;
 27         for (int i=1;i<=n;i++) b[i]=0;
 28         for (int i=1;i<=n;i++) b[rank[i]]++;
 29         for (int i=1;i<=n;i++) b[i]+=b[i-1];
 30         for (int i=n;i>=1;i--) d[b[rank[c[i]]]--]=c[i];
 31         x=0;
 32         for (int i=1;i<=n;i++)
 33         {
 34             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++;
 35             c[d[i]]=x;
 36         }
 37         for (int i=1;i<=n;i++) rank[i]=c[i];
 38         if (x==n) break; 
 39         j=j*2;
 40     }
 41 }
 42 void get_height(int n)
 43 {
 44     int x=0;
 45     for (int i=1;i<=n;i++) sa[rank[i]]=i;
 46     for (int i=1;i<=n;i++)
 47     {
 48         if (x!=0) x--;
 49         int j=sa[rank[i]-1];
 50         while (i+x<=n&&j+x<=n&&s[i+x]==s[j+x]) x++;
 51         height[rank[i]]=x;
 52     }
 53     height[1]=0;
 54 }
 55 void build(int x,int l,int r)
 56 {
 57     e[x].p=-inf; e[x].v=inf;
 58     if (l==r) return;
 59     int mid=(l+r)/2;
 60     build(x*2,l,mid); build(x*2+1,mid+1,r);
 61 }
 62 void insert1(int id,int l,int r,int x,int y,int z)
 63 {
 64     if (l==x&&r==y) { e[id].v=min(e[id].v,z); return; }
 65     int mid=(l+r)/2;
 66     if (y<=mid) insert1(id*2,l,mid,x,y,z);
 67     else if (x>mid) insert1(id*2+1,mid+1,r,x,y,z);
 68          else insert1(id*2,l,mid,x,mid,z),insert1(id*2+1,mid+1,r,mid+1,y,z);
 69 }
 70 void insert2(int id,int l,int r,int x,int y,int z)
 71 {
 72     if (l==x&&r==y) { e[id].p=max(e[id].p,z); return; }
 73     int mid=(l+r)/2;
 74     if (y<=mid) insert2(id*2,l,mid,x,y,z);
 75     else if (x>mid) insert2(id*2+1,mid+1,r,x,y,z);
 76          else insert2(id*2,l,mid,x,mid,z),insert2(id*2+1,mid+1,r,mid+1,y,z);
 77 }
 78 void query(int id,int l,int r,int x)
 79 {
 80     mn=min(mn,e[id].v); wz=max(wz,e[id].p);
 81     if (l==r) return;
 82     int mid=(l+r)/2;
 83     if (x<=mid) query(id*2,l,mid,x);
 84     else query(id*2+1,mid+1,r,x);
 85 }
 86 int main()
 87 {
 88     scanf("%s",str+1); n=strlen(str+1);
 89     for (int i=1;i<=n;i++) s[i]=str[i]-'a'+1;
 90     get_SA(n,30); 
 91     get_height(n);
 92     build(1,1,n);
 93     for (int i=1;i<=n;i++)
 94     {
 95         int x=sa[i],len=max(height[i],height[i+1]);
 96         if (len==n-x+1) continue;
 97         insert1(1,1,n,x,x+len,len+1);
 98         if (x+len+1<=n) insert2(1,1,n,x+len+1,n,x);
 99     }
100     for (int i=1;i<=n;i++)
101     {
102         mn=inf; wz=-inf; 
103         query(1,1,n,i);
104         printf("%d\n",min(mn,i-wz+1));
105     }
106     return 0;
107 }

 

posted @ 2018-07-14 09:19  BEYang_Z  阅读(211)  评论(0编辑  收藏  举报