5 最长回文串(2021-06-23)

5. 最长回文子串

链接:https://leetcode-cn.com/problems/longest-palindromic-substring/

题目描述见链接内容。

解法1:动态规划

(1)第一步:定义数组元素的含义

dp[i][j]定义为从从i开始,到j结束的字符串是不是一个回文串,是的话填充true

(2)第二步:找出数组元素之间的关系式

如果一个子串是回文串,那么去掉首尾字符,剩下的子串也一定是回文串,那么就可以得到状态转义方程:

dp[i][j] = dp[i + 1][j - 1] && (s[i] === s[j])

要注意的是转移方向,由于去掉的首字符和尾字符,相当于向内部收缩,所以i需要+1,而j需要-1

(3)第三步:找出初始值

需要找出边界条件,当字符串长度为1时,那么它一定是个回文串,当字符串长度为2时,如果首尾两个字符串相等那么它就是回文串,不需要考虑数dp[i + 1][j - 1]的情况

(4)具体实现

在实现的时候,我一开始按照常规的对ij进行内外双层遍历,但是发现,在遍历到j比较大时,较小的值并没有计算出来,需要将外层遍历改为遍历子串的长度

var longestPalindrome = function (s) {
  const length = s.length;

  if (length < 2) {
    return s;
  }

  let result = s[0];
  const dp = [];

  // 遍历子串长度
  for (let L = 2; L <= length; L++) {
    // 遍历起始位置
    for (let i = 0; i < length; i++) {
      // 初始化
      if (!Array.isArray(dp[i])) {
        dp[i] = [];
      }

      // j - i + 1  = L
      let j = L + i - 1;

      // 右边界越界了,退出循环
      if (j >= length) {
        break;
      }

      // 如果首尾字符串不等,那么一定不是回文串
      if (s[i] !== s[j]) {
        dp[i][j] = false;
      } else {
        // 首尾字符串相等,如果长度是1或者2,那么一定是回文串
        if (j - i <= 2) {
          dp[i][j] = true;
        } else {
          // 如果长度大于2,就需要考虑去掉首尾后的情况了
          dp[i][j] = dp[i + 1][j - 1];
        }
      }

      // 如果是一个回文串,那么看一下与现在回文串谁更长
      if (dp[i][j] && j - i + 1 > result.length) {
        result = s.slice(i, j + 1);
      }
    }
  }

  return result;
};
  • 时间复杂度:${O(N^2)}$
  • 空间复杂度:${O(N^2)}$
  • 执行用时:2012ms, 在所有JavaScript提交中击败了15.210%的用户,内存消耗:66.2MB,在所有JavaScript提交中击败了22.03%的用户

解法2:中心扩展法

所有的回文串都是中心对称的,所以枚举所有的回文中心,并尝试向两侧扩展,直到无法扩展,此时的回文串长度就是次回文中心下的最长回文串长度。

在遍历时要注意,回文串的长度可以是奇数(一个元素为中心)也可以是偶数(两个元素为中心),所以要处理两次

var longestPalindrome = function (s) {
  const length = s.length;

  if (length === 0 || length === 1) {
    return s;
  }

  let result = s[0];

  for (let i = 0; i < length; i++) {
    // 一个元素为中心的回文串
    const s1 = expandFromCenter(s, i, i),
      // 两个元素为中心的回文串
      s2 = expandFromCenter(s, i, i + 1),
      maxS = s1.length > s2.length ? s1 : s2;

    if (maxS.length > result.length) {
      result = maxS;
    }
  }

  return result;
};

function expandFromCenter(s, l, r) {
  const len = s.length;

  while (l >= 0 && r < len && s[l] === s[r]) {
    l--;
    r++;
  }
  // 正常slice的参数应该是(l, r + 1),但是由于最后一次l--和r++是多余的,所以l应该+1,而r应该-1
  return s.slice(l + 1, r);
}
  • 时间复杂度:${O(N^2)}$
  • 空间复杂度:${O(1)}$
  • 执行用时:100ms, 在所有JavaScript提交中击败了97.61%的用户,内存消耗:40.6MB,在所有JavaScript提交中击败了65.64%的用户
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值