动态规划-回文串问题

一. 回文子串

  1. 思路:例如在字符串 “abccfb” 中,我们很容易能够看出有7个子串分别为 “a”, “b”, “c”, “c”, “f”, “b”, “cc”, 7个子串,但要注意子串是连续的,例如他的子串 “bccfb”, 我们不能将其中的 “bccb” 视为答案。要确定 [0,s.length) 区间的字符串是否为回文子串,就要确定其子区间是否为回文子串,即 [ i , j ] i, j ∈ [ 0, s.length )。
    此时有三种情况:
    ①. i === j, 此时为单个字符串必为子串。
    ②. i === j + 1 && s[ i ] === s[ j ]. 此时两个字符也为回文子串。
    ③. i > j + 1 && s[ i ] === s[ j ] 此时该区间能否为回文子串取决于该区间的子区间 [ i - 1][ j + 1 ] 是否为回文子串。
    ④. s[ i ] !== s[ j ] 则区间子串不为回文子串。
  2. 初始化:在上面的推到中我们确定了地推公式与 j + 1i - 1 有关,则我们的遍历方式要与其相符。dp[0][0]表示第0个字符,自然是回文子串,我们将其初始化为true

具体代码如下

var countSubstrings = function (s) {
  let len = s.length;
  //初始化dp数组
  let dp = new Array(len).fill(0).map(() => new Array(len).fill(false));
  dp[0][0] = true;
  //记录回文子串的个数
  let res = 1;
  for (let i = 1; i < len; i++) {
    for (let j = i; j >= 0; j--) {
    //遍历顺序根据思路确定
      if (s[i] === s[j]) {
        if (i <= j + 1) {
          dp[i][j] = true;
        } else {
          dp[i][j] = dp[i - 1][j + 1];
        }
      } else {
        dp[i][j] = false;
      }

      if (dp[i][j] === true) {
        res = res + 1;
      }
    }
  }
  return res;
};

二. 最长回文序列

  1. 思路: 与第一题类似,有区别的地方在与回文序列表示回文子串是可以不连续的,上方的举例 “bccfb” 我们可以将 "bccb"视为一个长度为4的回文子序列。 此时我们要记录的是数据而不只是某区间是否为回文子串了。
  2. 初始化:dp[0][0]表示第0个字符,自然是回文子串,我们将其初始化为长度1

具体代码如下


var longestPalindromeSubseq = function (s) {
  let len = s.length;
  let dp = new Array(len).fill(0).map(() => new Array(len).fill(0));
  dp[0][0] = 1;
  for (let i = 1; i < len; i++) {
    for (let j = i; j >= 0; j--) {
      if (s[i] === s[j]) {
        if (i <= j + 1) {
          //同一个指针为1, 相隔1为2
          dp[i][j] = i - j + 1;
        } else {
          //加上两头的字符,所以加2
          dp[i][j] = dp[i - 1][j + 1] + 2;
        }
      } else {
      	//此时应该取子区间较大的那一个
        dp[i][j] = Math.max(dp[i - 1][j], dp[i][j + 1]);
      }
    }
  }

  return dp[len - 1][0];
};

小结
在写此类问题的时候要看清题目要我们做的是序列还是子串,判断其是否连续。注意根据递推公式的符号方向来判断遍历的顺序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值