携手创作,共同成长!这是我参与「掘金日新计划 · 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,最大为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 - 动态转移方程:比如:'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),理解转移方程很重要