晚上咖啡也不错
正题
给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。
回文字符串 是正着读和倒过来读一样的字符串。
子字符串 是字符串中的由连续字符组成的一个序列。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:s = "abc"
输出:3
解释:三个回文子串: "a", "b", "c"
示例 2:
输入:s = "aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
解析:
有示例可以看出单个字符也可以看做是一个回文子串。将问题分解成两个小问题:
- 判断字符串是否为回文字符串
- 每一个字符最多可以组成多少个回文字符串
以上图作为示例:
首先每个字符当作一个回文字符串,然后使用动态规划,遍历求出每个字符串可能组成的回文个数。
dpList 初始状态为 长度为入参字符串长度的每个元素都为1的数组。 因为每个单个字符串都可看作是一个回文。 然后遍历各个长度的字符串。
第一步,写一个方法,去判断是否为回文 check
const check = function (s) {
let left = 0
let right = s.length - 1
while(left < right) {
if (s[left] !== s[right]) {
return false
}
left ++;
right --
}
return true
}
以上采用的是双指针的算法,当然也可以转成字符串数组然后反转,再转回字符串去和原字符串比较是否相等。但是经过反复测试,通过字符串数组反转的方法消耗的性能和时间比双指针算法要多,大家有兴趣可以去尝试一下。
第二步,使用动态规划求的每一个字符能组成回文的个数。
完整代码:
/**
* @param {string} s
* @return {number}
*/
const check = function (s) {
let left = 0
let right = s.length - 1
while(left < right) {
if (s[left] !== s[right]) {
return false
}
left ++;
right --
}
return true
}
var countSubstrings = function(s) {
const dpList = new Array(s.length).fill(1)
for (let i = 1 ; i < s.length ; i++) {
for (let j = 0; j < i; j++) {
let str = s.slice(j, i + 1)
if (check(str)) {
dpList[i] = dpList[i] + 1
}
}
}
let res = 0
dpList.forEach(n => {
res+=n
})
return res
};
又是一个动态规划解决问题的案例。