leetcode hot100之回文子串(647)解析

178 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情


前言

  • leetcode hot100,是大厂面试高频题,也是必刷算法题。精选了100道LeetCode上最热门的题目,适合初识算法与数据结构的新手和想要在短时间内高效提升的人,按照官方说的,熟练掌握这 100 道题,就具备了代码世界通行的基本能力。
  • 此题可以使用暴力解法,也可以使用动态规划

leetcode647题(回文子串)

本文来讲hot100第647题,回文子串

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例:

输入: s = "abc"
输出: 3
解释: 三个回文子串: "a", "b", "c"
输入: s = "aaa"
输出: 6
解释: 6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

分析

通过分析题目可知几个信息点

  1. 求的是回文子串的数量
  2. 子字符串,长度最小为1,最大为s的长度

我们分析题意可以使用暴力循环解决

  • 暴力法,双层循环,外层循环我们遍历每个字符
  • 内层循环我们从当前字符的下标开始,结尾不断的+1循环

代码

暴力解法

var countSubstrings = function(s) {
  const len = s.length
  let count = 0
  const helper = (l, r) => {
    while (l < r) {
      if (s[l] !== s[r]) return false
      l ++
      r --
    } 
    return true
  }
  for (let i = 0; i < len; i ++) {
    for (let j = i; j < len; j ++) {
      if (helper(i, j)) {
        count ++
      }
    }
  }
  return count
};
  • 在这里我们定义了一个helper方法,帮助我们判断子字符串是否回文子串,如果是则计数+1,如果不是则继续循环。
  • 但是这种方法使用了三层循环,很容易超时,我们进一步优化

暴力解法(优化helper)

由于我们计算的是回文,回文的左右两侧一定是相等的,所以我们在内部循环的时候,我们可以按顺序和逆序累加字符串,下面是代码优化。(外面都是一样的代码)

  for (let i = 0; i < len; i ++) {
    let s1 = '', s2 = ''
    for (let j = i; j < len; j ++) {
      // 这里是核心,如果当
      s1 = s1 + s[j]
      s2 = s[j] + s2
      if (s1 === s2) {
          count ++
      }
    }
  }

动态规划

动态规划将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法 动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。

  1. 创建一个矩形(二维数组,长宽都是)
  2. 从每个字符开始,向前遍历,从当前字符串之前的每个位置截取到这个字符串如果为回文字符,
    那么+1
  3. 动态转移方程:比如:'abcba' 来说,只要 开头的 'a' === 结尾的 'a' 并且中间的 'bcb' 是回文子串, 那么 'abcba' 就是回文子串
var countSubstrings = function(s) {
  let n = s.length,
      dp = new Array(n),
      count = 0;
      
  for (let i = 0; i < n; i++) {
    dp[i] = new Array(n).fill( false );
  }
  
  for (let i = 0; i < n; i++) {
    for (let j = i; j >= 0; j--) {
      if (s.charAt(j) === s.charAt(i) && (
         i - j <= 1 || dp[j + 1][i - 1]
      )) {
        dp[j][i] = true;
        count++;
      }
    }
  }
  
  return count;
};

结语

  • 暴力解法时间复杂度:O(n^2)或者O(n^3)
  • 动态规划时间复杂度:O(n^2),理解转移方程很重要