这章要比上章的AC自动机要难理解。

这里首先要理解基数排序:基数排序与桶排序,计数排序【详解】

下面通过这个积累信心:五分钟搞懂后缀数组!后缀数组解析以及应用(附详解代码)

下面认真研读下这篇: [转载]后缀数组算法总结   里面的图片真Nice

然后找到适合再加深认识的:后缀数组学习笔记  算法合集之《后缀数组——处理字符串的有力工具》

后缀数组:通过对字符串后缀的处理得到子串的信息。

这里我通过借鉴上面的blog和训练指南写下了一段学习程序,还有在线多模板匹配的二分查找未完成。

用代码和测试数据的结果对比第三篇文章里的图片,有助于理解后缀数组sa的构建过程。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int maxn = ;
  4. char s[maxn]; //字符串s,每个字符值[0,m-1]
  5. int c[maxn]; //辅助数组
  6. int x[maxn]; //rank数组,下标表示关键字的位置,值表示关键字的名次
  7. // x代表每个后缀的前缀的种类
  8. int y[maxn]; //表示排序后的第二关键字数组,下标表示名次
  9. //值表示第二关键字的首字符位置
  10. int sa[maxn];
  11. /* 在构造完前表示关键字数组:下标表示名次,值表示关键字首字符位置,值相同时名次根据在原串中的相对位置决定
  12. * 构造完成后表示后缀数组,下标表示名次,值表示后缀的首字符位置
  13. */
  14. void show_sort_to_get_first_sa(int n){
  15. cout << "第一次计数排序结果:" << endl;
  16. for(int i=; i<n; i++){
  17. cout << "第" << i << "位" << endl;
  18. cout << "大小 = " << x[i] << " " << char(x[i]) << endl;
  19. cout << "该后缀的首字符的下标(或称后缀-): " << sa[i] << endl;
  20. // aaaaaabb
  21. //
  22. }
  23. }
  24. void show_second_key(int n){
  25. cout << "y的结果" << endl;
  26. for(int i=; i<n; i++){
  27. cout << y[i] << " ";
  28. }cout << endl;
  29. /*
  30. * xaaaaabb
  31. * 70234516
  32. */
  33. }
  34. void show_last(int n){
  35. cout << "\nsa的终值:\n";
  36. for(int i=; i<n; i++){
  37. cout << sa[i] << " ";
  38. }cout << endl;
  39. }
  40. bool cmp(int *f, int x, int y, int k){
  41. return f[x]==f[y] && f[x+k]==f[y+k];
  42. }
  43. void build_sa(int m, int n){
  44. //每个字符值为[0,m-1]
  45. //字符串长度为n
  46. for(int i=; i<=m; i++){
  47. c[i] = ;
  48. }
  49. for(int i=; i<n; i++){
  50. c[x[i] = s[i]]++;
  51. } //x是s中字符的键值//c存储各键值在输入数组中出现的次数
  52. for(int i=; i<m; i++){
  53. c[i] += c[i-];
  54. }
  55. /*
  56. * 计算前缀和,即可知小于小于每个数的个数
  57. * aabaaaab=11211112 --> c[1]=6, c[2]=2
  58. * 计算前缀和--> c[2]=8
  59. */
  60. for(int i=n-; i>=; i--){
  61. sa[--c[ x[i]]] = i;
  62. }//满足第一关键字的同时满足第二关键字
  63. /* 以上代码
  64. * 对后缀的第一位进行计数排序。
  65. */
  66. show_sort_to_get_first_sa(n);
  67.  
  68. for(int k=; k<=n; k<<=){
  69. // k:当前子字符串长度
  70. int p = ;
  71. //当前的第二关键字可直接由上次的sa得到
  72. for(int i=n-k; i<n; i++){
  73. y[p++] = i;
  74. }// 长度越界,第二关键字为0
  75. for(int i=; i<n; i++){
  76. if(sa[i] >= k){
  77. y[p++] = sa[i] - k; //-k后移上次排序的结果
  78. //y存储对第二关键字排序的结果
  79. }
  80. }//使用有第k个字符的字符串,直接排序第二关键字
  81. //以上结合两个关键字进行总排序
  82.  
  83. show_second_key(n);
  84.  
  85. //基数排序第一关键字
  86. for(int i=; i<=m; i++){
  87. c[i] = ;
  88. }
  89. for(int i=; i<n; i++){
  90. c[x[y[i]]]++;
  91. }
  92. for(int i=; i<m; i++){
  93. c[i] += c[i-];
  94. }
  95. for(int i=n-; i>=; i--){
  96. sa[ --c[ x[ y[i]]]] = y[i];
  97. //y[i] = 0;
  98. } //更新sa,
  99. swap(x, y); //用y暂存上一轮的x(rank)
  100. /*
  101. * 集和两个关键字排总序
  102. * 完成了关键字的合并
  103. */
  104. p = ; x[sa[]] = ;
  105. //用已经完成的sa来更新与它互逆的rank(x)
  106. for(int i=; i<n; i++){
  107. x[sa[i]] = cmp(y,sa[i],sa[i-],k)?p-:p++;
  108. }
  109. if(p >= n) break; //即使倍增,sa也不会改变
  110. m = p;
  111. }
  112.  
  113. show_last(n); //
  114.  
  115. }
  116. int rank[maxn]; //rank[string's num] = rank
  117. int height[maxn]; //height[rank] = len of lcp
  118. void getheight(int n){
  119. for(int i=; i<n; i++){
  120. rank[sa[i]] = i;
  121. }//计算每个字符串字典序的排名
  122. /*
  123. * h[i]:第i个位置开始的后缀与排名在其前一名的后缀的最长公共前缀
  124. * k=0:从首字符开始看第i个字符串和第j个字符串前面有多少是相同的
  125. * k!=0, height[rank[i]] = height[rank[i-1]] - 1,LCP长度最少是k-1,
  126. * 于是从首字符后面k-1个字符开始检查
  127. */
  128. for(int i=, k=; i<n; i++){
  129. if(k) k--;
  130. /* 若k不为0, h[i]>=h[i-1]-1,最长公共前缀长度至少是k-1,从首字符后k-1开始检查
  131. * h[i-1]-1是一系列h值的最小值,包括后缀i和排在它前一个的后缀p的LCP长度
  132. */
  133. int j = sa[rank[i] - ];
  134. while(j+k<n && i+k<n && s[j+k]==s[i+k]) k++;
  135. //计算height[rank[i]],LCP(rank[i],rank[i]-1)
  136. height[rank[i]] = k;
  137. }
  138. }
  139. int main(){
  140. freopen("in.txt", "r", stdin);
  141. int n, m;
  142. cin >> n >> m;
  143. cin >> s;
  144. build_sa(m, n);
  145. for(int i=; i<n; i++){
  146. for(int j=sa[i]; j<n; j++){
  147. cout << s[j] << " ";
  148. }cout << endl;
  149. }
  150. getheight(n);
  151. return ;
  152. }
  153. /*
  154. in.txt
  155. 8 200
  156. aabaaaab
  157. */

还没开始做题,这个知识点怕暂时要凉了。

后缀数组--summer-work之我连模板题都做不起的更多相关文章

  1. AC自动机--summer-work之我连模板题都做不出

    这章对现在的我来说有点难,要是不写点东西,三天后怕是就一无所有了. 但写这个没有营养的blog的目的真的不是做题或提升,只是学习学习代码和理解一些概念. 现在对AC自动机的理解还十分浅薄,这里先贴上目 ...

  2. SPOJ Distinct Substrings(后缀数组求不同子串个数,好题)

    DISUBSTR - Distinct Substrings no tags  Given a string, we need to find the total number of its dist ...

  3. UOJ35 后缀数组(模板)

    #35. 后缀排序 这是一道模板题. 读入一个长度为 nn 的由小写英文字母组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置.位置编号为 1 ...

  4. 【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)

    [BZOJ5304][HAOI2018]字串覆盖(后缀数组,主席树,倍增) 题面 BZOJ 洛谷 题解 贪心的想法是从左往右,能选就选.这个显然是正确的. 题目的数据范围很好的说明了要对于询问分开进行 ...

  5. hdu 4622 Reincarnation(后缀数组)

    hdu 4622 Reincarnation 题意:还是比较容易理解,给出一个字符串,最长2000,q个询问,每次询问[l,r]区间内有多少个不同的字串. (为了与论文解释统一,这里解题思路里sa数组 ...

  6. poj2774 Long Long Message 后缀数组求最长公共子串

    题目链接:http://poj.org/problem?id=2774 这是一道很好的后缀数组的入门题目 题意:给你两个字符串,然后求这两个的字符串的最长连续的公共子串 一般用后缀数组解决的两个字符串 ...

  7. Codeforces VK Cup 2015 A.And Yet Another Bracket Sequence(后缀数组+平衡树+字符串)

    这题做得比较复杂..应该有更好的做法 题目大意: 有一个括号序列,可以对其进行两种操作: ·        向里面加一个括号,可以在开头,在结尾,在两个括号之间加. ·        对当前括号序列进 ...

  8. LUOGU P2408 不同子串个数(后缀数组)

    传送门 解题思路 后缀数组求本质不同串的裸题.\(ans=\dfrac{n(n+1)}{2} -\sum height[i]\). 代码 #include<iostream> #inclu ...

  9. hdu 1711 KMP算法模板题

    题意:给你两个串,问你第二个串是从第一个串的什么位置開始全然匹配的? kmp裸题,复杂度O(n+m). 当一个字符串以0为起始下标时.next[i]能够描写叙述为"不为自身的最大首尾反复子串 ...

随机推荐

  1. PyTorch 启程&拾遗

    1..Tensors are similar to NumPy’s ndarrays, with the addition being that Tensors can also be used on ...

  2. MySQL--------SQL优化审核工具实战

    1. 背景 SQLAdvisor是由美团点评公司技术工程部DBA团队(北京)开发维护的一个分析SQL给出索引优化建议的工具.它基于MySQL原生态词法解析,结合分析SQL中的where条件.聚合条件. ...

  3. 在cubemx中使用freertos中的注意事项

    就是使用信号量等rtos自带特性的时候,务必先初始化然后在发生信号量或接收. 而且在中断中发送信号量或队列的时候,务必把使能中断的语句放在初始化freertos之后,尤其是cubemx生成的代码,默认 ...

  4. 使用PHP读取PHP文件并输出到屏幕上

    看完这篇文章,你一定忘不掉htmlentities的用法 背景 今天有个需求,就是一个PHP开发的网址中,有一个页面可以提供给用户修改已经存在的PHP文件中的代码,并POST到服务器上保存. 每次将读 ...

  5. springboot2.0入门(一)----springboot 简介

    一.springboot解决了什么? 避免了繁杂的xml配置,框架自动帮我们完成了相关的配置,当我们需要进行相关插件集成的时候,只需要将相关的starter通过相关的maven依赖引进,并可以进行相关 ...

  6. 记录 vant Picker 选择器,实现三级联动,传对应省市区code值

    最近使用vant UI写移动端,感觉还不错 功能挺全的,带的还有省市区三级联动. 但是 突然遇到一个 产品要传的 省市区的code码,还和vant的 邮编不一样,我*****. 看了一下vant UI ...

  7. Unable to find the requested .Net Framework Data Provider

    换了个系统后发现VS2010和VS2012都有同样问题,在SQL EXPLORER 里连不上SQL Server,这也导致了打不开 dbml文件,会报错: The operation could no ...

  8. 洛谷P2110 欢总喊楼记

    洛谷题目链接 乱搞qwq 我们其实可以找规律,对于每个数$x$,我们先求出从$1$~$x$中有多少符合条件的,记为$sum[x]$,那么类似于前缀和,答案自然就是$sum[r]-sum[l-1]$了 ...

  9. Java进阶知识11 Hibernate多对多单向关联(Annotation+XML实现)

    1.Annotation 注解版 1.1.应用场景(Student-Teacher):当学生知道有哪些老师教,但是老师不知道自己教哪些学生时,可用单向关联 1.2.创建Teacher类和Student ...

  10. 小米oj 数组差(挺好的题)

     数组差 序号:#46难度:困难时间限制:1000ms内存限制:10M 描述 给定一个整数数组,找出两个不重叠的子数组A和B,使两个子数组元素和的差的绝对值 |SUM(A) - SUM(B)| 最大. ...