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的每一位的最短识别子串分别有多长。
一般地,对于一个字符串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
第二个点 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 }