动态规划算法

原文:告别动态规划,连刷 40 道题,我总结了这些套路,看不懂你打我(万字长文)

1.最长回文字符串

 思路:回文天然具有「状态转移」性质:一个长度严格大于 22 的回文去掉头尾字符以后,剩下的部分依然是回文。反之,如果一个字符串头尾两个字符都不相等,那么这个字符串一定不是回文。「动态规划」的方法根据这样的性质得到。

第 1 步:定义状态
dp[i][j] 表示:子串 s[i..j] 是否为回文子串,这里子串 s[i..j] 定义为左闭右闭区间,即可以取到 s[i] 和 s[j]。

第 2 步:思考状态转移方程
根据头尾字符是否相等,需要分类讨论:

dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1]
说明:

「动态规划」的「自底向上」求解问题的思路,很多时候是在填写一张二维表格。由于 s[i..j] 表示 s 的一个子串,因此 i 和 j 的关系是 i <= j,只需要填这张表格对角线以上的部分;
看到 dp[i + 1][j - 1] 就需要考虑特殊情况:如果去掉 s[i..j] 头尾两个字符子串 s[i + 1..j - 1] 的长度严格小于 22(不构成区间),即 j - 1 - (i + 1) + 1 < 2j−1−(i+1)+1<2 时,整理得 j - i < 3j−i<3,此时 s[i..j] 是否是回文只取决于 s[i] 与 s[j] 是否相等。结论也比较直观:j - i < 3j−i<3 等价于 j - i + 1 < 4j−i+1<4,即当子串 s[i..j]s[i..j] 的长度等于 22 或者等于 33 的时候,s[i..j] 是否是回文由 s[i] 与 s[j] 是否相等决定。


第 3 步:考虑初始化
单个字符一定是回文串,因此把对角线先初始化为 true,即 dp[i][i] = true。根据第 2 步的说明:当 s[i..j] 的长度为 22 时,只需要判断 s[i] 是否等于 s[j],所以二维表格对角线上的数值不会被参考。所以不设置 dp[i][i] = true 也能得到正确结论。

第 4 步:考虑输出
一旦得到 dp[i][j] = true,就记录子串的「长度」和「起始位置」。没有必要截取,这是因为截取字符串也有性能消耗。

var longestPalindrome = function(s) {
    var len = s.length
    if(len < 2) {
        return s
    }

    var dp = new Array(len).fill(0).map(i=>new Array(len).fill(true));

    var start = 0
    var maxlength = 1
    //从下往上 ,从右往左遍历 ,而不能从上往下遍历,
    //因为二维数组中的数值都被赋值为true,从上往下,
    //当要获取dp[i+1][j-1]的时候会直接返回true,然后就会导致输出错误
    for (let i = len-1; i >= 0; i--) {
        for(let j = i ; j <len; j++) {
            if (s[i] !== s[j]) {
                dp[i][j] = false
            }else {
                if (j-i<3) {
                    dp[i][j] = true
                }else {
                    dp[i][j] = dp[i+1][j-1]
                }
            }


            if(dp[i][j] === true && j-i+1>maxlength) {
                start = i
                maxlength = j-i+1
            }
        }
    }

    return s.substring(start,start + maxlength)
};

补充:es6建立二维数组方法

        创建长度为len的二维数组,并填写初值为true

const dp = new Array(len).fill(0).map(i=>new Array(len).fill(true));

        语法解析:

   Array(len)自然就是定义10个元素的数组

   fill()方法将一个数组的所有元素从开始索引填充到具有静态值的结束索引,语法为:

arr.fill(value)

arr.fill(value, start)

arr.fill(value, start, end)

  Array(10).fill(0)就是声明一个值全为 0 的数组

  map()方法就比较熟悉了,用来创建一个新数组,其结果是该数组中的每个元素调用一个提供的函数,语法为:

const new_array = arr.map(callback[, thisArg])

callback的三个参数为currentValue(数组中正在处理的当前元素),index(数组中正在处理的当前元素的索引),array(map 方法被调用的数组)。

所以开头的那段代码,就是用来创建一个长度为len,数值全为true的二维数组

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值