集训讲字符串的时候我唯一想出正解的题……

链接

BZOJ 2865

题面

给出一个长度为n (n <= 5e5) 的字符串,对于每一位,求包含该位的、最短的、在原串中只出现过一次的子串。

题解

“只出现过一次”,想到后缀数组,后缀数组可以求出以第i位开头的最短的在原串中只出现过一次的子串——它的长度是min(height[rank[i]], height[rank[i] + 1) + 1。

所以我们枚举每个位置i,找到这个串,然后考虑它的贡献:

对于这个串之内的位置,答案可以用这个串的长度更新;

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

这两个分别用线段树维护即可。

  1. #include <cstdio>
  2. #include <cmath>
  3. #include <cstring>
  4. #include <algorithm>
  5. using namespace std;
  6. typedef long long ll;
  7. #define enter putchar('\n')
  8. #define space putchar(' ')
  9. template <class T>
  10. void read(T &x){
  11. char c;
  12. bool op = 0;
  13. while(c = getchar(), c > '9' || c < '0')
  14. if(c == '-') op = 1;
  15. x = c - '0';
  16. while(c = getchar(), c >= '0' && c <= '9')
  17. x = x * 10 + c - '0';
  18. if(op) x = -x;
  19. }
  20. template <class T>
  21. void write(T x){
  22. if(x < 0) putchar('-'), x = -x;
  23. if(x >= 10) write(x / 10);
  24. putchar('0' + x % 10);
  25. }
  26. const int N = 500005, INF = 0x3f3f3f3f;
  27. int n, sa[N], rnk[N], buf1[N], buf2[N], buc[N], height[N];
  28. int data[2][4*N], lazy[2][4*N], pos[N];
  29. char s[N];
  30. void pushdown(int h, int k){
  31. if(lazy[h][k] == INF) return;
  32. lazy[h][k << 1] = min(lazy[h][k << 1], lazy[h][k]);
  33. lazy[h][k << 1 | 1] = min(lazy[h][k << 1 | 1], lazy[h][k]);
  34. data[h][k << 1] = min(data[h][k << 1], lazy[h][k]);
  35. data[h][k << 1 | 1] = min(data[h][k << 1 | 1], lazy[h][k]);
  36. lazy[h][k] = INF;
  37. }
  38. void modify(int h, int k, int l, int r, int ql, int qr, int x){
  39. if(ql <= l && qr >= r){
  40. data[h][k] = min(data[h][k], x);
  41. lazy[h][k] = min(lazy[h][k], x);
  42. return;
  43. }
  44. int mid = (l + r) >> 1;
  45. if(ql <= mid) modify(h, k << 1, l, mid, ql, qr, x);
  46. if(qr > mid) modify(h, k << 1 | 1, mid + 1, r, ql, qr, x);
  47. data[h][k] = min(data[h][k << 1], data[h][k << 1 | 1]);
  48. }
  49. void pushdown_all(int k, int l, int r){
  50. if(l == r) return (void)(pos[l] = k);
  51. pushdown(0, k), pushdown(1, k);
  52. int mid = (l + r) >> 1;
  53. pushdown_all(k << 1, l, mid);
  54. pushdown_all(k << 1 | 1, mid + 1, r);
  55. }
  56. void suffix_sort(){
  57. int m = 128, *x = buf1, *y = buf2;
  58. for(int i = 0; i <= m; i++) buc[i] = 0;
  59. for(int i = 1; i <= n; i++) buc[x[i] = s[i]]++;
  60. for(int i = 1; i <= m; i++) buc[i] += buc[i - 1];
  61. for(int i = n; i; i--) sa[buc[x[i]]--] = i;
  62. for(int k = 1, p = 0; k <= n && p < n; k *= 2, m = p, p = 0){
  63. for(int i = n - k + 1; i <= n; i++) y[++p] = i;
  64. for(int i = 1; i <= n; i++) if(sa[i] > k) y[++p] = sa[i] - k;
  65. for(int i = 0; i <= m; i++) buc[i] = 0;
  66. for(int i = 1; i <= n; i++) buc[x[y[i]]]++;
  67. for(int i = 1; i <= m; i++) buc[i] += buc[i - 1];
  68. for(int i = n; i; i--) sa[buc[x[y[i]]]--] = y[i];
  69. swap(x, y), x[sa[1]] = 1, p = 1;
  70. for(int i = 2; i <= n; i++)
  71. x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? p : ++p;
  72. }
  73. for(int i = 1; i <= n; i++) rnk[sa[i]] = i;
  74. for(int i = 1, k = 0; i <= n; i++){
  75. if(rnk[i] == 1) continue;
  76. int j = sa[rnk[i] - 1];
  77. if(k) k--;
  78. while(i + k <= n && j + k <= n && s[i + k] == s[j + k]) k++;
  79. height[rnk[i]] = k;
  80. }
  81. }
  82. int main(){
  83. scanf("%s", s + 1);
  84. n = strlen(s + 1);
  85. suffix_sort();
  86. memset(data, INF, sizeof(data));
  87. memset(lazy, INF, sizeof(lazy));
  88. for(int i = 1; i <= n; i++){
  89. int len = max(height[rnk[i]], height[rnk[i] + 1]);
  90. if(i + len <= n) modify(0, 1, 1, n, i, i + len, len + 1);
  91. if(i + len < n) modify(1, 1, 1, n, i + len + 1, n, 1 - i);
  92. }
  93. pushdown_all(1, 1, n);
  94. for(int i = 1; i <= n; i++)
  95. write(min(data[0][pos[i]], i + data[1][pos[i]])), enter;
  96. return 0;
  97. }

BZOJ 2865 字符串识别 | 后缀数组 线段树的更多相关文章

  1. bzoj 2865 字符串识别 —— 后缀数组

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2865 唯一出现的子串就是每个后缀除去和别的后缀最长的 LCP 之外的前缀: 所以用这个更新一 ...

  2. bzoj 2865 字符串识别——后缀数组

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2865 做出 ht[ ] 之后,sa[ ] 上每个位置和它前面与后面取 LCP ,其中较大的长 ...

  3. 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)

    点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...

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

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

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

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

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

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

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

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

  8. BZOJ 5496: [2019省队联测]字符串问题 (后缀数组+主席树优化建图+拓扑排序)

    题意 略 分析 考场上写了暴力建图40分溜了-(结果只得了30分) 然后只要优化建边就行了 首先给出的支配关系无法优化,就直接A向它支配的B连边. 考虑B向以B作为前缀的所有A连边,做一遍后缀数组,两 ...

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

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

随机推荐

  1. English_word_learning

    这次报名参加了学院的21天打卡活动,说实话,也是想给自己一个积累的平台. 毕竟,真的有时候感觉挺弱的 有的人用了一年考完了四六级,而有人却用四年还未考完. 听到有一位学长因为自己的四级成绩没有达到48 ...

  2. Scala_数据类型

    Scala与Java有着相同的数据类型,Scala数据类型都是对象,Scala中没有类似Java中那样的原始类型. Scala 的基本数据类型有: Byte,Short,Int,Long 和 Char ...

  3. 20155336 虎光元《网络攻防》Exp2后门原理与实践

    20155336 虎光元<网络攻防>Exp2后门原理与实践 一.实验内容 (1)使用netcat获取主机操作Shell,cron启动 (0.5分) (2)使用socat获取主机操作Shel ...

  4. mfc CAnimateCtrl

    知识点: CAnimateCtrl成员函数 播放avi动画 一. CAnimateCtrl成员函数 Autoplay; CAnimateCtrl ::成员函数 Open 打开avi视频 Play 播放 ...

  5. Javascript与后台相互访问

    (1)Javascript访问C#后台变量或函数 A.通过<%=%>的形式访问 var str = "<%=GetStr() %>"; var str = ...

  6. CS190.1x-ML_lab5_pca_student

    这次lab也是最后一次lab了,前面两次lab介绍了回归和分类,特别详细地介绍了线性回归和逻辑回归,这次的作业主要是非监督学习--降维,主要是PCA.数据集是神经科学的数据,来自于Ahrens Lab ...

  7. 爬虫利器_you-get

    用Python做爬虫也很久了,今天分享一个轻巧的爬虫库:you-get you-get 是用 Python3写成的视频,图片,音频下载工具,堪称盗链,爬虫神器.其支持的网站,都是直接破解其算法,直接算 ...

  8. jmeter:正则表达式的使用

    Jmeter中正则关联的使用是可以提取动态变化数据进行传递:关联的方式和提取器有多种,这篇先讲解正则表达式怎么来关联(?) 在需要获取数据的http请求上添加后置处理器 比如提取百度title值: 正 ...

  9. LeetCode-97.交错字符串

    给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的. 示例 1: 输入: s1 = "aabcc", s2 = "dbbca&quo ...

  10. 关于github的使用心得

    https://github.com/JavaLizheng/test git常用命令: git config :配置git git add:更新working directory中的文件至stagi ...