后缀数组很久很久以前就出现了,具体的概念读者自行搜索,小菜仅略知一二,不便讨论。

本文通过寻找两个字符串的最长公共子字符串,演示了后缀数组的经典应用。

首先需要说明,小菜实现的这个后缀数组算法,并非标准,只是借鉴了其中的思想。

小菜实现的算法,有两个版本,第一个是空间换时间,第二个是时间换空间。

空间换时间版本

  1. /*
  2. 利用后缀数组获取两个字符串最长公共子字符串
  3. 空间换时间版本
  4. @params
  5. s1 String,要分析的字符串
  6. s2 String,要分析的字符串
  7. norepeat Boolean,是否对结果进行去重,默认true
  8. */
  9. function commonSubString(s1, s2, norepeat){
  10. var norepeat = norepeat == undefined ? true : norepeat,
  11. array = suffixArray(s1, 1).concat(suffixArray(s2, 2)),
  12. maxLength = 0,
  13. maxStrings = [],
  14. tempLength = 0,
  15. i = 0,
  16. length = 0,
  17. result = {};
  18.  
  19. //排序,根据字符串排序,直接比较即可
  20. array.sort(function(s1, s2){
  21. return (s1.s == s2.s) ? 0 : (s1.s > s2.s) ? 1 : -1;
  22. });
  23.  
  24. //寻找最长公共子字符串
  25. for(i = 0, length = array.length - 1; i < length; i++){
  26. tempLength = commonLength(array[i].s, array[i+1].s);
  27. if(array[i].g != array[i+1].g){
  28. if(maxLength == tempLength){
  29. maxStrings.push(array[i]);
  30. }
  31. if(maxLength < tempLength){
  32. maxLength = tempLength;
  33. maxStrings = [];
  34. maxStrings.push(array[i]);
  35. }
  36. }
  37. }
  38.  
  39. //构造结果
  40. result.length = maxLength;
  41. result.contents = [];
  42. for(i in maxStrings){
  43. result.contents.push(maxStrings[i].s.substring(0, maxLength));
  44. }
  45.  
  46. //去重
  47. if(norepeat){
  48. result.contents = norepeatArray(result.contents);
  49. }
  50.  
  51. return result;
  52.  
  53. /*
  54. 获取字符串的后缀数组
  55. */
  56. function suffixArray(s, g){
  57. var array = [],
  58. i = 0,
  59. length = s.length;
  60. for(i = 0; i < length; i++){
  61. array.push({
  62. s: s.substring(i),
  63. g: g //加分组是为了保证最长公共子字符串分别来自两个字符串
  64. });
  65. }
  66.  
  67. return array;
  68. }
  69. /*
  70. 获取最大匹配长度
  71. */
  72. function commonLength(s1, s2){
  73. var slength = s1.length > s2.length ? s2.length : s1.length,
  74. i = 0;
  75.  
  76. //循环次数=较短的字符串长度
  77. for(i = 0; i < slength; i++){
  78. //逐位比较
  79. if(s1.charAt(i) != s2.charAt(i)){
  80. break;
  81. }
  82. }
  83.  
  84. return i;
  85. }
  86.  
  87. /*
  88. 字符串数组去重,不会影响原数组,返回一个新数组
  89. */
  90. function norepeatArray(array){
  91. var _array = array.slice(0),
  92. map = {},
  93. i = 0,
  94. key = "";
  95.  
  96. //将内容作为散列的key
  97. for(i in _array){
  98. map[_array[i]] = 1;
  99. }
  100.  
  101. //提取散列key,重新填充到数组
  102. _array.splice(0, _array.length);
  103. for(key in map){
  104. _array.push(key);
  105. }
  106.  
  107. return _array;
  108. }
  109. }

时间换空间版本

  1. /*
  2. 利用后缀数组获取两个字符串最长公共子字符串
  3. 时间换空间版本
  4. @params
  5. s1 String,要分析的字符串
  6. s2 String,要分析的字符串
  7. norepeat Boolean,是否对结果进行去重,默认true
  8. */
  9. function commonSubStringPro(s1, s2, norepeat){
  10. var norepeat = norepeat == undefined ? true : norepeat,
  11. array = suffixArray(s1, 1).concat(suffixArray(s2, 2)),
  12. maxLength = 0,
  13. maxStrings = [],
  14. tempLength = 0,
  15. i = 0,
  16. length = 0,
  17. result = {};
  18.  
  19. //排序,根据实际内容排序,不能根据指针排序
  20. array.sort(function(s1, s2){
  21. var ts1 = s1.str.substring(s1.index),
  22. ts2 = s2.str.substring(s2.index);
  23. return (ts1 == ts2) ? 0 : (ts1 > ts2) ? 1 : -1;
  24. });
  25.  
  26. //寻找最长公共子字符串
  27. for(i = 0, length = array.length - 1; i < length; i++){
  28. tempLength = commonLength(array[i], array[i+1]);
  29. if(array[i].group != array[i+1].group){
  30. if(maxLength == tempLength){
  31. maxStrings.push(array[i]);
  32. }
  33. if(maxLength < tempLength){
  34. maxLength = tempLength;
  35. maxStrings = [];
  36. maxStrings.push(array[i]);
  37. }
  38. }
  39. }
  40.  
  41. //构造结果
  42. result.length = maxLength;
  43. result.contents = [];
  44. for(i in maxStrings){
  45. result.contents.push(maxStrings[i].str.substr(maxStrings[i].index, maxLength));
  46. }
  47.  
  48. //去重
  49. if(norepeat){
  50. result.contents = norepeatArray(result.contents);
  51. }
  52.  
  53. return result;
  54.  
  55. /*
  56. 获取字符串的后缀数组
  57. 只存指针,不存实际内容
  58. */
  59. function suffixArray(s, g){
  60. var array = [],
  61. i = 0,
  62. length = 0;
  63. for(i = 0, length = s.length; i < length; i++){
  64. array.push({
  65. index: i,
  66. str: s, //这里仅仅存放的是字符串指针,不会创建多个副本
  67. group: g //加分组是为了保证最长公共子字符串分别来自两个字符串
  68. });
  69. }
  70.  
  71. return array;
  72. }
  73. /*
  74. 获取最大匹配长度
  75. */
  76. function commonLength(s1, s2){
  77. var slength = 0,
  78. i = 0;
  79.  
  80. //循环次数=较短的字符串长度
  81. slength = (s1.str.length - s1.index) > (s2.str.length - s2.index) ? (s2.str.length - s2.index) : (s1.str.length - s1.index);
  82. for(i = 0; i < slength; i++){
  83. //逐位比较
  84. if(s1.str.substr(i + s1.index, 1) != s2.str.substr(i + s2.index, 1)){
  85. break;
  86. }
  87. }
  88.  
  89. return i;
  90. }
  91.  
  92. /*
  93. 字符串数组去重,不会影响原数组,返回一个新数组
  94. */
  95. function norepeatArray(array){
  96. var _array = array.slice(0),
  97. map = {},
  98. i = 0,
  99. key = "";
  100.  
  101. //将内容作为散列的key
  102. for(i in _array){
  103. map[_array[i]] = 1;
  104. }
  105.  
  106. //提取散列key,重新填充到数组
  107. _array.splice(0, _array.length);
  108. for(key in map){
  109. _array.push(key);
  110. }
  111.  
  112. return _array;
  113. }
  114. }

为啥会有两个版本呢?小菜原本只写了空间换时间版本,这个版本实现复杂度低,但是有一个明显的弊端,它占用了太多无谓的内存,分析数据量不大的时候,可以完美胜任,一旦数据量达到一定程度,它表现出来的不仅仅是执行时间变长,而是根本无法运行,除非有足够大的内存。

基于以上思考,小菜发现在生成后缀数组的时候,根本没必要保存实际字符串,只需记录位置信息即可,这样一来,内存中的大量字符串,均变成一个个整型数值,在做比较的时候,我们甚至不需要还原字符串,直接用位置去截取单个字符即可,最终内存极大节省,这就是时间换空间版本。

通过时间换空间,带来的不仅仅是节省内存,而是一种质的变化,从不可能变成可能,现在,无论有多大的数据量,只需很小一部分内存,即可支持程序运转,就算运行时间再长,它也是可行的,不会直接崩溃。当然,现在的CPU运行速度已经很快了。

希望对读者有所启发,至于具体代码,就不多说了,注释很详细。

使用后缀数组寻找最长公共子字符串JavaScript版的更多相关文章

  1. hdu 1403 Longest Common Substring(最长公共子字符串)(后缀数组)

    http://acm.hdu.edu.cn/showproblem.php?pid=1403 Longest Common Substring Time Limit: 8000/4000 MS (Ja ...

  2. HDU 1403 Longest Common Substring(后缀数组,最长公共子串)

    hdu题目 poj题目 参考了 罗穗骞的论文<后缀数组——处理字符串的有力工具> 题意:求两个序列的最长公共子串 思路:后缀数组经典题目之一(模版题) //后缀数组sa:将s的n个后缀从小 ...

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

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

  4. URAL 1517 Freedom of Choice(后缀数组,最长公共字串)

    题目 输出最长公共字串 #define maxn 200010 int wa[maxn],wb[maxn],wv[maxn],ws[maxn]; int cmp(int *r,int a,int b, ...

  5. Palindrome--poj 1159(最长公共子字符串+滚动数字)

    http://poj.org/problem?id=1159 题目大意:  给你一个n  代表n个字符   第二行给你一个字符串  求使这个字符串变成回文字符串 最少需要添加多少个字符 分析:   原 ...

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

    Reference:IOI2009论文 http://www.cnblogs.com/ziyi--caolu/p/3192731.html #include "stdio.h" # ...

  7. Long Long Message (poj2774 后缀数组求最长公共子串)

    Long Long Message Time Limit: 4000MS   Memory Limit: 131072K Total Submissions: 19206   Accepted: 79 ...

  8. POJ 2774 后缀数组:查找最长公共子

    思考:其实很easy.就在两个串在一起.通过一个特殊字符,中间分隔,然后找到后缀数组的最长的公共前缀.然后在两个不同的串,最长是最长的公共子串. 注意的是:用第一个字符串来推断是不是在同一个字符中,刚 ...

  9. poj 1743 男人八题之后缀数组求最长不可重叠最长重复子串

    Musical Theme Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 14874   Accepted: 5118 De ...

随机推荐

  1. 纯jsp用户登录系统

    用纯jsp技术实现用户登录系统,需要用到三个.jsp文件.在文本目录下新建三个.jsp文件,分别命名为login.jsp,logincl.jsp和wel.jsp. 1.login.jsp文件用来放界面 ...

  2. ubuntu与win10互换硬盘

    实例:将sdb上的ubuntu转移至sda,将sda上的win转移至sdb1. 备份资料2. 制作老毛桃PE盘3. 格式化sda4. dd if=/dev/sdb of=/dev/sda ,将sdb克 ...

  3. Hadoop2.6.0的事件分类与实现

    前言 说实在的,在阅读Hadoop YARN的源码之前,我对于java枚举的使用相形见绌.YARN中实现的事件在可读性.可维护性.可扩展性方面的工作都值得借鉴. 概念 在具体分析源码之前,我们先看看Y ...

  4. XML文件的读写

    using System; using System.Collections.Generic; using System.Xml; namespace COMMON { public class Xm ...

  5. ZOJ3791_An Easy Game

    给出两个等长的字符串,每次需要改变m个数字,每次必须改变k个数字,求从第一个串变化到第二个串的方案数. DP.f[i][j]改变i步后,有j个位置被改变的方案数.然后直接枚举当前改变的几个位置是前面重 ...

  6. Mac 系统下的环境变量

    1.查看电脑环境变量   -->echo $PATH 2. 新建环境变量   sudo vim ~/.bash_profile  输入密码 3. 按 I ,编辑新的环境变量地址,保存 退出 :w ...

  7. 【转】怎样查出SQLServer的性能瓶颈

    怎样查出SQLServer的性能瓶颈 --王成辉翻译整理,转贴请注明出自微软BI开拓者[url]www.windbi.com[/url]--原帖地址 如果你曾经做了很长时间的DBA,那么你会了解到SQ ...

  8. javascript里面的数组,json对象,动态添加,修改,删除示例

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  9. vim的批量注释与删除注释

    vim的批量注释与删除注释 方法一:块选择模式 批量注释: Ctrl + v 进入块选择模式,然后移动光标选中你要注释的行,再按大写的I进入行首插入模式输入注释符号如 // 或 #,输入完毕之后,Vi ...

  10. WEB响应布局

    [15/06月,15] em是相对长度单位.相对于当前对象内文本的字体尺寸.如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸.(引自CSS2.0手册) 任意浏览器的默认字体高都是1 ...