动态规划问题(2)——寻找最长公共子串

题目

给定两个字符串,求出它们的最长公共字串git

var str1="abcdefg";
var str2="xyzabcd";

说明:好比在单词"abcdefg"和"abcdefg"它们的最长公共子序列是"abcd"。寻找最长子序列经常使用于遗传学中,用于使用核苷酸碱基的首字母对DNA的描述(这段话从网上抄的)。github

最长公共子串和最长公共子序列的区别。数组

最长公共子串和最长公共子序列的区别为:子串是串的一个连续的部分,子序列则是从不改变序列的顺序,而从序列中去掉任意的元素而得到新的序列;也就是说,子串中字符的位置必须是连续的,子序列则能够没必要连续。微信

这道题,想了很长时间,终于慢慢的把题目作出来了,接下来的内容可能会很难理解,也许我比较笨吧至少对我来讲想了很久。我会尽可能结合图文去展现解题的思路,也能够本身想一想,而后在看接下来的内容。优化

暴力破解

其实看到这个问题咱们直接能够用暴力的方式解决这个问题。给定两个字符串A和B,咱们能够经过从A的第一个字符开始与B对应的每个字符进行对比的方式找到最长的公共字串。若是此时没有找到匹的字母,则移动到A的第二个字符处,而后从B的第一个字符处进行对比,以此类推。因为下面的内容较多,爆力方法我这里就不写了。spa

动态规划

咱们回顾一下动态规划的解题思路:code

  1. 从底部开始解决问题,将全部小问题解决掉,而后合并成一个总体的解决方案。rem

  2. 使用一个数组创建一张表,用于存放被分解成众多子问题的解。字符串

那基于这两点咱们首先要分解这个问题,怎么分解呢?源码

第一步: 最小的是每一个字符,因此要把分解成单个的字符去匹配每一个单个的字符。所以这里咱们能够获得下面这一张表:

字符串匹配

匹配为1,不匹配为0,这个时候咱们就分解成为每一个字符的匹配状况,由此获得。

第二步: 每一个子问题有了,这个时候咱们要创建一个数组去存放每一个子问题的解。关键问题在于,咱们怎么样把每一个子问题的解存起来,而且能够获得咱们想要的结果。对表作一些处理,初始化一个二维数组:

var arr = new Array(str1.length + 1);
for (var i = 0; i <= str1.length + 1; i++) {
    arr[i] = new Array(str2.length + 1);
    for (var j = 0; j <= str2.length + 1; j++) {
        arr[i][j] = 0;
    }
}

获得以下的表:

image

图中咱们能够看到行和列都多加了一个而且都是0,这是为了判断上一个是否相等的操做,后面你就会明白它的意思了。

对初始化的二维数组坐些处理,获得以下图:

构建存放数组

一开始看到这个图的时候你可能会很懵逼,若是你已经看明白了,那你就跳过我这段废话吧。咱们应该一直的告诉本身,新建的这个数组是存放每一个子问题的解,首先要明确的是找出最长字串,有哪些方式能够作到把字串从原来的字符串中截取,因此要知道起始位置和字串的长度。从图中能够看到的红字就是存放这个位置的最优解字串的长度,因此应该创建两个变量去存储其实位置的index 和最大长度 maxLen 。获得一下代码:

var maxLen = 0;
var index = 0;
for(var i = 0; i <= str1.length; i++){
    for(var j = 0; j <= str2.length; j++){
        if(i == 0 || j == 0){
            arr[i][j] = 0
        }else{
            if (str1[i] == str2[j] && str1[i - 1] == str2[j - 1]) {
                arr[i][j] = arr[i - 1][j - 1] + 1;
            }else{
                arr[i][j] = 0;
            }
        }
        if(arr[i][j] > maxLen){
            maxLen = arr[i][j];
            index = i;
        }
    }
}

简单的解释一下,这里要明确连续的字串的位置,是二维数组对角线上的值,这点明确了不少问题就好理解了。

上面的代码把最主要的两个参数找到了,那接下来就是选择其中的一个字符串去截取字串:

var str = "";
if(maxLen == 0){
    return "";
}else{
    for(var k = index - maxLen; k < maxLen; k++){
        str += str1[k];
    }
    return str;
}

下面贴上完整的代码,你也能够去个人github上查看源码;

function LCS(str1, str2){
    var maxLen = 0;
    var index = 0;

    var arr = new Array();
    for (var i = 0; i <= str1.length + 1; i++) {
        arr[i] = new Array();
        for (var j = 0; j <= str2.length + 1; j++) {
            arr[i][j] = 0;
        }
    }

    for(var i = 0; i <= str1.length; i++){
        for(var j = 0; j <= str2.length; j++){
            if(i == 0 || j == 0){
                arr[i][j] = 0
            }else{
                if (str1[i] == str2[j] && str1[i - 1] == str2[j - 1]) {
                    arr[i][j] = arr[i - 1][j - 1] + 1;
                }else{
                    arr[i][j] = 0;
                }
            }
            if(arr[i][j] > maxLen){
                maxLen = arr[i][j];
                index = i;
            }
        }
    }

    var str = "";
    if(maxLen == 0){
        return "";
    }else{
        for(var k = index - maxLen; k < maxLen; k++){
            str += str1[k];
        }
        return str;
    }
}
var str1="abcdefg";
var str2="xyzabcd";
LCS(str1, str2)     // abcd

到此为止咱们终于找到了最长公共字串。到这里咱们须要想一想能不能在优化了,虽然这样解决问题,可是引入了一个二维数组,在两个字符串都较大的状况下不是很划算,是否能够进一步优化?答案是能够的,须要改变一下策略,是否能够创建一个一维数组就能够解决问题了呢,这里晚了先写这么多把,以后我会在公众号中贴出暴力解法和动态规划的优化解法,欢迎持续关注个人公众号得到一手的信息。

欢迎关注微信公众帐号查看最新文章

欢迎关注微信公众帐号查看最新文章

相关文章
相关标签/搜索