[关闭]
@y20070316 2017-03-10T06:15:12.000000Z 字数 3371 阅读 903

字符串

专题练习 OI



书写原则

  对于每一种题目,首先有一个Todo-List构成的题目总览。如果题目简单,则在总览处写题解,否则在之后写详细题解。

后缀数组

题目总览

【xsy1828】string

题意:给定个字符串,求出一个最长的串,使得这个串或者这个串的回文在所有个字符串中都出现。

分析:
对于个字符串的处理,我们就把所有的串以及其回文串在一起。
求后缀数组sa以及height数组。
二分答案,按进行分类,若个数等于n则OK。

关于个串串在一起的实现:

  1. char s[L]; int len;
  2. int star[N],fini[N],slen[N];
  3. int own[L];

后缀数组的实现:

  1. int sa[L],rk[L],ht[L];
  2. int sum[L],tsa[L],trk[L];
  3. void Prework(void) {
  4. memset(sa,0,sizeof sa);
  5. memset(rk,0,sizeof rk);
  6. memset(ht,0,sizeof ht);
  7. memset(sum,0,sizeof sum);
  8. memset(tsa,0,sizeof tsa);
  9. memset(trk,0,sizeof trk);
  10. int lim=512,p;
  11. F(i,1,len)
  12. sum[rk[i]=s[i]]++;
  13. F(i,1,lim)
  14. sum[i]+=sum[i-1];
  15. D(i,len,1)
  16. sa[sum[rk[i]]--]=i;
  17. rk[sa[1]]=p=1;
  18. F(i,2,len) {
  19. if (s[sa[i]]!=s[sa[i-1]])
  20. p++;
  21. rk[sa[i]]=p;
  22. }
  23. lim=p;
  24. for (int j=1;lim!=len;j<<=1) {
  25. p=0;
  26. memset(tsa,0,sizeof tsa);
  27. F(i,len-j+1,len)
  28. tsa[++p]=i;
  29. F(i,1,len)
  30. if (sa[i]>j)
  31. tsa[++p]=sa[i]-j;
  32. memset(sum,0,sizeof sum);
  33. memmove(trk,rk,sizeof rk);
  34. F(i,1,len)
  35. sum[rk[i]=trk[tsa[i]]]++;
  36. F(i,1,lim)
  37. sum[i]+=sum[i-1];
  38. D(i,len,1)
  39. sa[sum[rk[i]]--]=tsa[i];
  40. rk[sa[1]]=p=1;
  41. F(i,2,len) {
  42. if (trk[sa[i]]!=trk[sa[i-1]]||trk[sa[i]+j]!=trk[sa[i-1]+j])
  43. p++;
  44. rk[sa[i]]=p;
  45. }
  46. lim=p;
  47. }
  48. p=0;
  49. F(i,1,len) {
  50. if (p>0)
  51. p--;
  52. while (s[i+p]==s[sa[rk[i]-1]+p])
  53. p++;
  54. ht[rk[i]]=p;
  55. }
  56. }

分类的实现:

  1. int Check(int lim) {
  2. tot=ind=0;
  3. memset(vis,0,sizeof vis);
  4. F(l,1,len) {
  5. int r=l;
  6. while (r+1<=len&&ht[r+1]>=lim)
  7. r++;
  8. ind++,tot=0;
  9. F(i,l,r)
  10. Add(sa[i],lim);
  11. if (tot==n)
  12. return 1;
  13. l=r;
  14. }
  15. return 0;
  16. }

【bzoj3238】差异

一个细节:
对于后缀数组,我们开的空间是

if (trk[sa[i]]!=trk[sa[i-1]]||trk[sa[i]+j]!=trk[sa[i-1]+j]) lim++;
为什么不会越界?

因为若,则
所以
所以不会越界。

部分代码:

  1. void Prework(void) {
  2. int lim=256;
  3. F(i,1,n) sum[rk[i]=s[i]]++;
  4. F(i,1,lim) sum[i]+=sum[i-1];
  5. D(i,n,1) sa[sum[rk[i]]--]=i;
  6. rk[sa[1]]=lim=1;
  7. F(i,2,n) {
  8. if (s[sa[i]]!=s[sa[i-1]]) //注意用s
  9. lim++;
  10. rk[sa[i]]=lim;
  11. }
  12. for (int j=1;lim!=n;j<<=1) {
  13. memset(tsa,0,sizeof tsa); int p=0;
  14. F(i,n-j+1,n) tsa[++p]=i;
  15. F(i,1,n) if (sa[i]>j) tsa[++p]=sa[i]-j;
  16. memset(sum,0,sizeof sum);
  17. memcpy(trk,rk,sizeof rk);
  18. F(i,1,n) sum[rk[i]=trk[tsa[i]]]++;
  19. F(i,1,lim) sum[i]+=sum[i-1];
  20. D(i,n,1) sa[sum[rk[i]]--]=tsa[i];
  21. rk[sa[1]]=lim=1;
  22. F(i,2,n) {
  23. if (trk[sa[i]]!=trk[sa[i-1]]||trk[sa[i]+j]!=trk[sa[i-1]+j]) lim++;
  24. rk[sa[i]]=lim;
  25. }
  26. }
  27. int p=0;
  28. F(i,1,n) {
  29. if (p>0) p--;
  30. while (s[i+p]==s[sa[rk[i]-1]+p]) p++;
  31. ht[rk[i]]=p;
  32. }
  33. }
  1. void XXX(void) {
  2. stk[top=0]=1;
  3. F(j,2,n) {
  4. while (top>0&&ht[j]<=ht[stk[top]]) {
  5. tmp-=(LL)ht[stk[top]]*(stk[top]-stk[top-1]);
  6. stk[top--]=0;
  7. }
  8. stk[++top]=j;
  9. tmp+=(LL)ht[j]*(j-stk[top-1]);
  10. ans+=tmp;
  11. }
  12. }

【bzoj4278】【ontak2015】Tasowanie

分析:每次肯定要尽可能选一个小一点的。一样大怎么办?比较字典序。可能会出现比较完的情况,所以在两个串的末尾都加一个INF。
把两个串接起来,后缀排序。

实现:在实现的时候由于数组没有开大一倍,RE了。
不明白为什么会RE。
但是不论如何,后缀数组的空间还是开大一倍好了。
以免出题人出错数据。

路合并也可做。
用个优先队列。

Hash

bzoj3098
bzoj3162
bzoj2085
bzoj1125
bzoj2803
bzoj1054
bzoj3207
bzoj3507
bzoj4337
bzoj2124
bzoj1862
bzoj1056
bzoj3067
bzoj3097
bzoj2258
bzoj3198
bzoj2795
bzoj1414
bzoj3574

KMP

bzoj1009
bzoj3670
bzoj3942
bzoj1355
bzoj1461
bzoj3620
bzoj3213
bzoj1511
bzoj1729
bzoj3899
bzoj3796

exKMP

Manacher

回文自动机

后缀自动机

后缀树

bzoj3238
bzoj3879
bzoj3998
bzoj2555

波兰表

AC自动机

bzoj2754
bzoj2434
bzoj2938
bzoj1030
bzoj3881
bzoj3940
bzoj3172
bzoj1195
bzoj2533
bzoj1009
bzoj1444
bzoj4327
bzoj1212
bzoj3028

Fail树

bzoj3881
bzoj2434

Trie

bzoj3689
bzoj1954
bzoj3439
bzoj3166
bzoj1819
bzoj2741
bzoj3906
bzoj4212
bzoj4567
bzoj4523

最小表示法

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注