题意

在 \(S\) 中找出 \(t\) 个子串满足 \(t_{i+1}\) 是 \(t_{i}\) 的子串,要让 \(t\) 最大。

\(|S| \leq 5\times 10^5\).

分析

  • 定义状态 \(f_{i}\) 表示从 \(i\) 出发能够得到的最长的 \(journey\) .

  • 容易得到最终的答案最右边的串长度一定可以是1.

  • 同时如果删掉没用的部分过后 \(t_i\) 的长度一定可以为 $t_{i+1} +1 $.

  • 如果在 \(i\) 位置存在长度为 \(k\) 的答案的话,将两边某一个字符在所有串中抠掉(还要舍去一个串),一定也存在长度为 \(k-1\) 的答案,所以答案单调。

  • 假设当前枚举的答案为 \(k\) ,只需要在 \([i+k,n]\) 这个区间中存在一个子串满足

\[S_{i,i+1\cdots i+k-2}=S_{j,j+1\cdots j+k-2}$$ 或者 $$S_{i+1,i+2\cdots i+k-1}=S_{j,j+1\cdots j+k-2}
\]

同时 \(f_j\geq k-1\) 的话,就说明 \(f_i\geq k\) .

  • 但是发现一定有 \(f_i\leq f_{i+1}+1\) ,所以暴力枚举每个位置的答案,不需要二分。

  • 那些满足 \(LCP(i,j) \geq k-1\) 的位置在 \(sa\) 数组中一定是一个区间,线段树维护最大值。

  • 总时间复杂度为 \(O(nlogn)\)。

代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].lst,v=e[i].to)
  4. #define rep(i,a,b) for(int i=a;i<=b;++i)
  5. #define repd(i,a,b) for(int i=a;i>=b;--i)
  6. #define pb push_back
  7. typedef long long LL;
  8. inline int gi(){
  9. int x=0,f=1;char ch=getchar();
  10. while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
  11. while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
  12. return x*f;
  13. }
  14. template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
  15. template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;}
  16. const int N=5e5 + 7;
  17. int n,ans;
  18. char s[N];
  19. int val[N<<2],f[N];
  20. #define Ls o<<1
  21. #define Rs o<<1|1
  22. void modify(int p,int l,int r,int o,int v){
  23. Max(val[o],v);
  24. if(l==r) return;
  25. int mid=l+r>>1;
  26. if(p<=mid) modify(p,l,mid,Ls,v);
  27. else modify(p,mid+1,r,Rs,v);
  28. }
  29. int query(int L,int R,int l,int r,int o){
  30. if(L<=l&&r<=R) return val[o];
  31. int mid=l+r>>1;
  32. if(R<=mid) return query(L,R,l,mid,Ls);
  33. if(L>mid) return query(L,R,mid+1,r,Rs);
  34. return max(query(L,R,l,mid,Ls),query(L,R,mid+1,r,Rs));
  35. }
  36. namespace SA{
  37. int x[N],y[N],c[N],sa[N],h[N],mi[N][20],Log[N];
  38. void getsa(int m){
  39. rep(i,1,m) c[i]=0;
  40. rep(i,1,n) c[x[i]=s[i]]++;
  41. rep(i,1,m) c[i]+=c[i-1];
  42. repd(i,n,1) sa[c[x[i]]--]=i;
  43. for(int k=1;k<=n;k<<=1){
  44. int p=0;
  45. for(int i=n;i>=n-k+1;--i) y[++p]=i;
  46. rep(i,1,n) if(sa[i]>k) y[++p]=sa[i]-k;
  47. rep(i,1,m) c[i]=0;
  48. rep(i,1,n) c[x[y[i]]]++;
  49. rep(i,1,m) c[i]+=c[i-1];
  50. repd(i,n,1) sa[c[x[y[i]]]--]=y[i];
  51. swap(x,y);p=1;x[sa[1]]=1;
  52. rep(i,2,n)
  53. x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p:++p;
  54. if(p>=n) break; m = p;
  55. }
  56. rep(i,1,n) x[sa[i]]=i;
  57. for(int i=1,j=0;i<=n;++i){
  58. if(j) --j;if(x[i]==1) continue;
  59. while(s[i+j]==s[sa[x[i]-1]+j]) ++j;
  60. h[x[i]]=j;
  61. }
  62. Log[1]=0;
  63. rep(i,2,n) Log[i]=Log[i>>1]+1;
  64. rep(i,1,n) mi[i][0]=h[i];
  65. for(int k=1;1<<k<=n;++k)
  66. for(int i=1;i+(1<<k)-1<=n;++i)
  67. mi[i][k]=min(mi[i][k-1],mi[i+(1<<k-1)][k-1]);
  68. }
  69. int rmq_query(int l,int r){
  70. l++;
  71. if(l>r) return n+1;
  72. int k=Log[r-l+1];
  73. return min(mi[l][k],mi[r-(1<<k)+1][k]);
  74. }
  75. int get1(int p,int up){
  76. int l=1,r=p;
  77. while(l<r){
  78. int mid=l+r>>1;
  79. if(rmq_query(mid,p)>=up) r=mid;
  80. else l=mid+1;
  81. }
  82. return l;
  83. }
  84. int get2(int p,int up){
  85. int l=p,r=n;
  86. while(l<r){
  87. int mid=l+r+1>>1;
  88. if(rmq_query(p,mid)>=up) l=mid;
  89. else r=mid-1;
  90. }
  91. return l;
  92. }
  93. }
  94. int main(){
  95. scanf("%d%s",&n,s+1);
  96. using namespace SA;
  97. getsa(129);
  98. f[n]=ans=1;
  99. for(int i=n-1,j=1;i;--i){
  100. for(++j;j;--j){
  101. if(i+j<=n) modify(x[i+j],1,n,1,f[i+j]);
  102. int l=get1(x[i+1],j-1),r=get2(x[i+1],j-1),fg=0;
  103. fg|=query(l,r,1,n,1)>=j-1;
  104. l=get1(x[i],j-1),r=get2(x[i],j-1);
  105. fg|=query(l,r,1,n,1)>=j-1;
  106. if(fg) break;
  107. }
  108. f[i]=j;
  109. Max(ans,f[i]);
  110. }
  111. printf("%d\n",ans);
  112. return 0;
  113. }

[CF1063F]String Journey[后缀数组+线段树]的更多相关文章

  1. Codeforces 1063F - String Journey(后缀数组+线段树+dp)

    Codeforces 题面传送门 & 洛谷题面传送门 神仙题,做了我整整 2.5h,写篇题解纪念下逝去的中午 后排膜拜 1 年前就独立切掉此题的 ymx,我在 2021 年的第 5270 个小 ...

  2. BZOJ 1396: 识别子串( 后缀数组 + 线段树 )

    这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...

  3. 【XSY1551】往事 广义后缀数组 线段树合并

    题目大意 给你一颗trie树,令\(s_i\)为点\(i\)到根的路径上的字符组成的字符串.求\(max_{u\neq v}(LCP(s_u,s_v)+LCS(s_u,s_v))\) \(LCP=\) ...

  4. Luogu4770 NOI2018你的名字(后缀数组+线段树)

    即求b串有多少个本质不同的非空子串,在a串的给定区间内未出现.即使已经8102年并且马上就9102年了,还是要高举SA伟大旗帜不动摇. 考虑离线,将所有询问串及一开始给的串加分隔符连起来,求出SA.对 ...

  5. BZOJ 2865 字符串识别 | 后缀数组 线段树

    集训讲字符串的时候我唯一想出正解的题-- 链接 BZOJ 2865 题面 给出一个长度为n (n <= 5e5) 的字符串,对于每一位,求包含该位的.最短的.在原串中只出现过一次的子串. 题解 ...

  6. bzoj 1396: 识别子串 && bzoj 2865: 字符串识别【后缀数组+线段树】

    根据height数组的定义,和当前后缀串i最长的相同串的长度就是max(height[i],height[i+1]),这个后缀贡献的最短不同串长度就是len=max(height[i],height[ ...

  7. BZOJ 2865 字符串识别(后缀数组+线段树)

    很容易想到只考虑后缀长度必须为\(max(height[rk[i]],height[rk[i]+1])+1\)(即\([i,i+x-1]\)代表的串只出现过一次)然后我正着做一遍反着做一遍,再取一个\ ...

  8. [CF653F] Paper task - 后缀数组,线段树,vector

    [CF653F] Paper task Description 给定一个括号序列,统计合法的本质不同子串的个数. Solution 很容易想到,只要在传统统计本质不同子串的基础上修改一下即可. 考虑经 ...

  9. BZOJ.1396.识别子串(后缀自动机/后缀数组 线段树)

    题目链接 SAM:能成为识别子串的只有那些|right|=1的节点代表的串. 设这个节点对应原串的右端点为r[i],则如果|right[i]|=1,即\(s[\ [r_i-len_i+1,r_i-le ...

随机推荐

  1. 测试笔试单选题(持续更新ing)

    1.在GB/T17544中,软件包质量要求包括三部分,即产品描述要求._____.程 序和数据要求.( A ) A.用户文档要求 B.系统功能要求 C.设计要求说明 D.软件配置要求 2.软件的六大质 ...

  2. MySQL索引原理及慢查询优化-zz

    https://tech.meituan.com/mysql-index.html MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色,但所 ...

  3. 动态修改css文件中,具体的class中的个别属性值。

    function setStyleSheetObjCssClassProperty(pStyleSheetObj, pSelectorText, pProperty, pValue) { var pS ...

  4. Centos7(Firewall)防火墙开启常见端口命令

    Centos7默认安装了firewalld,如果没有安装的话,则需要YUM命令安装:firewalld真的用不习惯,与之前的iptable防火墙区别太大,但毕竟是未来主流讲究慢慢磨合它的设置规则: 安 ...

  5. 4星|《门口的野蛮人2》:美国宝万之争专业户KKR公司的疯狂借债收购史

    门口的野蛮人2:KKR与资本暴利的崛起(珍藏版) 英文版是1992年出的.主要内容是1977-1998年之间KKR在美国的杠杆收购简史.从KKR创立开始,讲到1990年KKR差点倒闭.国内A股市场上前 ...

  6. [LOJ 2720][BZOJ 5417][UOJ 395][NOI 2018]你的名字

    [LOJ 2720][BZOJ 5417][UOJ 395][NOI 2018]你的名字 题意 给定一个大串 \(S\) 以及 \(q\) 次询问, 每次询问给定一个串 \(T\) 和区间 \([l, ...

  7. react如何引入外部文件的整理

    1 引入组件 首先就应该是安装了,安装在环境中后通过 import { Select, Button, Icon, QueueAnim } from 'antd'; 就可以把需要的组件引用进来了~ 2 ...

  8. shp转oracle spatial

    2010年12月1日  终于搞定了shp到oracle spatial,说下步骤和感受吧! 1 XP系统:转换工具的下载(shp2sdo.exe ):下载后把此文件复制到PATH变量包含的目录下(E: ...

  9. HDU5629:Clarke and tree(DP,Prufer)

    Description Input Output Sample Input Sample Output Solution 题意:给你$n$个点,还有每个点的度数,问你任选$i(1\leq i \leq ...

  10. BZOJ2744:[HEOI2012]朋友圈(最大团,乱搞)

    Description 在很久很久以前,曾经有两个国家和睦相处,无忧无虑的生活着.一年一度的评比大会开始了,作为和平的两国,一个朋友圈数量最多的永远都是最值得他人的尊敬,所以现在就是需要你求朋友圈的最 ...