JavaScript求最大公共子串的两种算法实现

作者:异步漫步

时间:2026-10-23

浏览:534

本文详解JavaScript中求最大公共子串的两种实现方式。首先介绍基于二维矩阵及一维数组优化的动态规划方法,通过状态转移方程减少空间复杂度;其次提供基于子串截取与indexOf判断的暴力枚举法,通过缩短基准字符串长度提升效率。文章包含完整代码示例与性能对比分析,适用于字符串处理场景。

文章目录

本文通过具体实例介绍在JavaScript环境中计算两个字符串最大公共子串的实现方案,供开发者参考与借鉴。

基于矩阵的动态规划解法

计算最大公共子串的经典思路通常借助矩阵结构。假设存在字符串“abcdefg”与“abcd”,可构建如下所示的对比矩阵。

a b c d e f g
a 1 0 0 0 0 0 0
b 0 1 0 0 0 0 0
c 0 0 1 0 0 0 0
d 0 0 0 1 0 0 0

遍历两个字符串的字符进行逐项比对,若字符匹配则对应位置标记为1,否则标记为0。随后寻找对角线上连续为1的最长序列,该序列即代表最大公共子串。观察上述矩阵可知,传统方法需使用二维数组,当字符串长度较大时,这种空间占用方式效率较低,因此存在优化空间。

可以通过调整策略来优化空间复杂度。若当前字符匹配,不再单纯标记为1,而是将其值设为对角线位置a[i-1, j-1](需满足i > 1且j > 1)的值加1。借助这一状态转移逻辑,仅需维护一个一维数组即可完成计算。

实现时,可将一个字符串视为“行”,另一个视为“列”,遍历比对字符。同时使用额外变量记录数组中的最大值及其对应的字符串起始索引。具体实现代码如下:


function LCS(str1, str2) {
 if (str1 === "" || str2 === "") {
  return "";
 }
 var len1 = str1.length;
 var len2 = str2.length;
 var a = new Array(len1);
 var maxLen = 0;
 var maxPos = 0;
 for (var i = 0; i < len1; i++) { //行
  for (var j = len2 - 1; j >= 0; j--) {//列
   if (str1.charAt(j) == str2.charAt(i)) {
    if (i === 0 || j === 0) {
     a[j] = 1;
    } else {
     a[j] = a[j - 1] + 1;
    }
   } else {
    a[j] = 0;
   }
   if (a[j] > maxLen) {
    maxLen = a[j];
    maxPos = j;
   }
  }
 }
 return str1.substr(maxPos - maxLen + 1, maxLen);
}

基于子串截取的暴力枚举优化

上述代码并非性能最优解,原因在于其必须等待双层循环完全执行完毕才能得出结果。是否存在执行效率更高的替代方案?

假设字符串a与b的长度分别为len1和len2,它们的最大公共子串长度必然小于等于Math.min(len1, len2)。此外,公共子串必须是连续的,且同时存在于a和b中。


function findMaxSubStr(s1,s2){
 var str= "",
  L1=s1.length,
  L2=s2.length;
 if (L1>L2){
  var s3=s1;
  s1=s2;
  s2=s3;
  s3 = null;
  L1=s2.length;
  L2 = s1.length;
 }
 for (var i=L1; i > 0; i--) {
  for (var j= 0; j <= L2 - i && j < L1; j++){
   str = s1.substr(j, i);
   if (s2.indexOf(str) >= 0) {
    return str;
   }
  }
 }
 return "";
}

首先比较s1与s2的长度,选取较短的字符串作为substr(idex, len),即从较短字符串中提取子串,然后判断该子串是否存在于较长的字符串中。若存在则直接返回结果;否则继续提取下一个子串进行判断。

完整的测试示例如下:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <title>www.jb51.net</title>
 <style type='text/css'>
 body {background-color:#fff;}
 </style>
 </head>
 <body>
<script type='text/javascript'>
function LCS(str1, str2) {
 if (str1 === "" || str2 === "") {
 return "";
 }
 var len1 = str1.length;
 var len2 = str2.length;
 var a = new Array(len1);
 var maxLen = 0;
 var maxPos = 0;
 for (var i = 0; i < len1; i++) { //行
 for (var j = len2 - 1; j >= 0; j--) {//列
 if (str1.charAt(j) == str2.charAt(i)) {
 if (i === 0 || j === 0) {
  a[j] = 1;
 } else {
  a[j] = a[j - 1] + 1;
 }
 } else {
 a[j] = 0;
 }
 if (a[j] > maxLen) {
 maxLen = a[j];
 maxPos = j;
 }
 }
 }
 return str1.substr(maxPos - maxLen + 1, maxLen);
}
function findMaxSubStr(s1,s2){
 var str= "",
 L1=s1.length,
 L2=s2.length;
 if (L1>L2) {
 var s3=s1;
 s1=s2;
 s2=s3;
 s3 = null;
 L1=s2.length;
 L2 = s1.length;
 }
 for (var i=L1; i > 0; i--) {
 for (var j= 0; j <= L2 - i && j < L1; j++){
   str = s1.substr(j, i);
   if (s2.indexOf(str) >= 0) {
 return str;
  }
  }
 }
 return "";
}
 !(function() {
 var tmpArr = [];
 for (var i = 97; i < 97 + 26; i++) {
 tmpArr.push(String.fromCharCode(i));
 }
 var s2 = tmpArr.join("");
 tmpArr.sort(function() {return Math.random() > 0.7;});
 var s1 = new Array(600).join(tmpArr.join(""));
 var date = getNow();
 alert( "消耗时间:" + (getNow() - date) + "秒 " + LCS(s1, s2));
 date = getNow();
 alert( "消耗时间:" + (getNow() - date) + "秒 " + findMaxSubStr(s1, s2) );
 })();
function getNow() {
 return new Date().getTime();
}
</script>
 </body>
</html>

如需了解更多JavaScript相关知识,可参考本站专题:《JavaScript数学运算用法总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript数组操作技巧总结》、《JavaScript排序算法总结》、《JavaScript遍历算法与技巧总结》、《JavaScript查找算法技巧总结》及《JavaScript错误与调试技巧总结》。

希望本文所述的算法实现能对读者的JavaScript编程工作提供有益帮助。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章