[关闭]
@y20070316 2017-04-24T00:41:33.000000Z 字数 2669 阅读 971

XSY 1920 - Mr. Panda and Fantastic Beats

题目精选 后缀数组 二分答案


题意

   Mr. Panda喜欢怪兽。为了找到怪兽,他必须念咒语。
  一天,Mr. Panda在竹林中找到一张纸,上面写着 个字符串。
  Mr. Panda的爸爸告诉他,这张纸上隐藏着咒语。
  在这 个字符串 中,咒语是满足以下两个条件的最短的字符串: ① 该字符串为 的子串 ② 该字符串不是 的子串。
  如果有解,输出字典序最小的一组。如果无解,输出 Impossible

  
  

分析

  考虑将所有字符串接起来,将相邻两个字符串中间用一个不同的字符接起来,求一次
  二分答案。假设当前的答案为 ,根据 数组将 进行分段。对于每个字符串 ,若 能作为答案,当且仅当它在块内满足要求。这是因为它和块外的 小于 ,所以外界对其无影响。
  块内满足要求,当且仅当每个坐标都属于第一个字符串,且字符串的长度至少为 。我们记录 为最大的坐标,以及 为最小的坐标,即可判定并求解。

实现

  1. #include <cstdio>
  2. #include <iostream>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. using namespace std;
  7. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  8. #define D(i,a,b) for (int i=(a);i>=(b);i--)
  9. const int N=300005;
  10. const int LIM=200;
  11. int nT;
  12. int n;
  13. int s[N]; int ns;
  14. int fini;
  15. int sa[N],rk[N],ht[N];
  16. int sum[N],trk[N],tsa[N];
  17. inline int rd(void) {
  18. int x=0,f=1; char c=getchar();
  19. for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
  20. for (;isdigit(c);c=getchar()) x=x*10+c-'0';
  21. return x*f;
  22. }
  23. void Prework(void) {
  24. memset(sa,0,sizeof sa);
  25. memset(rk,0,sizeof rk);
  26. memset(ht,0,sizeof ht);
  27. memset(sum,0,sizeof sum);
  28. memset(trk,0,sizeof trk);
  29. memset(tsa,0,sizeof tsa);
  30. int lim=LIM+ns;
  31. F(i,1,ns) sum[rk[i]=s[i]]++;
  32. F(i,1,lim) sum[i]+=sum[i-1];
  33. D(i,ns,1) sa[sum[rk[i]]--]=i;
  34. rk[sa[1]]=lim=1;
  35. F(i,2,ns) {
  36. if (s[sa[i]]!=s[sa[i-1]]) lim++;
  37. rk[sa[i]]=lim;
  38. }
  39. for (int j=1;lim!=ns;j<<=1) {
  40. int p=0; memset(tsa,0,sizeof tsa);
  41. F(i,ns-j+1,ns) tsa[++p]=i;
  42. F(i,1,ns) if (sa[i]>j) tsa[++p]=sa[i]-j;
  43. memset(sum,0,sizeof sum);
  44. memcpy(trk,rk,sizeof rk);
  45. F(i,1,ns) sum[rk[i]=trk[tsa[i]]]++;
  46. F(i,1,lim) sum[i]+=sum[i-1];
  47. D(i,ns,1) sa[sum[rk[i]]--]=tsa[i];
  48. rk[sa[1]]=lim=1;
  49. F(i,2,ns) {
  50. if (trk[sa[i]]!=trk[sa[i-1]]||trk[sa[i]+j]!=trk[sa[i-1]+j]) lim++;
  51. rk[sa[i]]=lim;
  52. }
  53. }
  54. int p=0;
  55. F(i,1,ns) {
  56. if (p>0) p--;
  57. while (s[i+p]==s[sa[rk[i]-1]+p]) p++;
  58. ht[rk[i]]=p;
  59. }
  60. }
  61. int Check(int len) {
  62. F(l,1,ns) {
  63. int r=l; while (r+1<=ns&&ht[r+1]>=len) r++;
  64. int mx=0; F(i,l,r) mx=max(mx,sa[i]);
  65. if (mx>fini) {
  66. l=r; continue;
  67. }
  68. int mn=ns+1; F(i,l,r) mn=min(mn,sa[i]);
  69. if (fini-mn+1>=len) return 1;
  70. l=r;
  71. }
  72. return 0;
  73. }
  74. void Make(char *t,int len) {
  75. F(l,1,ns) {
  76. int r=l; while (r+1<=ns&&ht[r+1]>=len) r++;
  77. int mx=0; F(i,l,r) mx=max(mx,sa[i]);
  78. if (mx>fini) {
  79. l=r; continue;
  80. }
  81. int mn=ns+1; F(i,l,r) mn=min(mn,sa[i]);
  82. if (fini-mn+1>=len) {
  83. F(i,l,r) if (fini-sa[i]+1>=len) {
  84. int tot=0;
  85. F(j,sa[i],sa[i]+len-1)
  86. t[++tot]=s[j];
  87. t[++tot]=0;
  88. break;
  89. }
  90. return;
  91. }
  92. l=r;
  93. }
  94. }
  95. int main(void) {
  96. #ifndef ONLINE_JUDGE
  97. freopen("sdchr.in","r",stdin);
  98. freopen("sdchr.out","w",stdout);
  99. #endif
  100. cin>>nT;
  101. F(c,1,nT) {
  102. memset(s,0,sizeof s); ns=fini=0;
  103. n=rd();
  104. F(i,1,n) {
  105. static char t[N]; scanf("%s",t+1);
  106. int nt=strlen(t+1);
  107. if (i==1) fini=nt;
  108. F(j,1,nt) s[++ns]=t[j];
  109. s[++ns]=i+LIM;
  110. }
  111. Prework();
  112. int l=1,r=fini;
  113. if (!Check(r)) {
  114. printf("Case #%d: Impossible\n",c); continue;
  115. }
  116. while (l<r) {
  117. int mid=(int)floor((l+r)/2.0);
  118. if (Check(mid))
  119. r=mid;
  120. else l=mid+1;
  121. }
  122. static char t[N]; Make(t,l);
  123. printf("Case #%d: %s\n",c,t+1);
  124. }
  125. return 0;
  126. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注