本人是一个一名前端菜🐔,正在努力加班加点学习中,看着大佬们写的文章、demo啥的,羡慕不已。盼望着大佬们哪天能给个内推机会啥的那就nice了。 最近刷leetcode刷到这个题目,也在网上看到了各类各样的解法,于湿乎我也尝试着写文章,记录一下学习中值得分享的内容html
第一次写文章,有不当之处还望各位大佬指出。前端
给定一个字符串 s,找到 s 中最长的回文子串。你能够假设 s 的最大长度为 1000。 git
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
github
示例 2:
输入: “amnbvcxzzxcvbnmb”
输出: “mnbvcxzzxcvbnm”
算法
最容易想到的就是暴力解法,即外面的两层循环找到全部子串,第三层循环判断子串是不是回文。方法的时间复杂度为O(n^2),空间复杂度为O(1)。编程
var longestPalindrome = function (s) {
let n = s.length;
if(n == 0) return ''; //字符串为空则返回空
if(n == 1) return s; //字符串为一个字符, 显然返回自身
let result = ''
for (let i = 0; i < n; i++) { //字符串长度超过2
for (let j = i + 1; j <= n; j++) {
let str = s.slice(i, j); //可获得全部子串
let f = str.split('').reverse().join(''); //对字符串利用数组方法倒序
if (str == f) { //判断是否为回文
result = str.length > result.length ? str : result;
}
}
}
return result;
}
console.log(longestPalindrome(str))
复制代码
很显然,此解法虽然最终可以获得结果,可是效率很低,在这个讲究高效编程的时代,这种方法是不可取的。 (此方法因为时间复杂度过高,在leetcode上提交时会提示 Time Limit Exceeded,而且提交不了)c#
动态规划(Dynamic Programming)是一种分阶段求解决策问题的数学思想。总结起来就是一句话,大事化小,小事化了。数组
1.最优化原理:若是问题的最优解所包含的子问题的解也是最优的,就称该问题具备最优子结构,即知足最优化原理。
2.无后效性:即某阶段状态一旦肯定,就不受这个状态之后决策的影响。也就是说,某状态之后的过程不会影响之前的状态,只与当前状态有关。
3.有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被屡次使用到。(该性质并非动态规划适用的必要条件,可是若是没有这条性质,动态规划算法同其余算法相比就不具有优点bash
大概了解了一下动态规划,下面让咱们来看具体代码学习
var longestPalindrome = function(s) {
let len = s.length;
let result;
let i,j,L;
let dp=Array(len).fill(0).map(x=>Array(len).fill(0));
//console.log(dp);
if(len<=1){
return s
}
// 只有一个字符的状况是回文
for(i = 0;i<len;i++){
dp[i][i] = 1
result = s[i]
}
// L是i和j之间的间隔数(由于间隔数从小到大渐增,因此大的间隔数总能包含小的间隔数)
// i j
// abcdcba.length = L 因此 L = j-i+1; => j = i+L-1;
for ( L = 2; L <= len; L++) {
// 从0开始
for ( i = 0; i <= len - L; i++) {
j = i + L - 1;
if(L == 2 && s[i] == s[j]) {
dp[i][j] = 1;
result = s.slice(i, i + L);
}else if(s[i] == s[j] && dp[i + 1][j - 1] == 1) {
dp[i][j] = 1
result = s.slice(i, i + L);
}
}
}
//console.log(result);
return result;
}
复制代码
方法的时间复杂度为O(n^2), 空间复杂度也为O(n^2), 效率上整体来讲相对暴力解法有很大的提高, 是一种不错的解法, 并且动态规划的应用场景不少, 想进一步学习的老铁能够点这里动态规划应用场景。
Manacher算法,又叫“马拉车”算法,能够在时间复杂度为O(n)的状况下求解一个字符串的最长回文子串长度的问题。
在进行Manacher算法时,字符串都会进行上面的进入一个字符处理,好比输入的字符为acbbcbds,用“#”字符处理以后的新字符串就是#a#c#b#b#c#b#d#s#。
var str = 'ddabbade'
const longestPalindrome = function (s) {
if (s.length == 1) {
return s
}
let str = '#' + s.split('').join('#') + '#'
let rl = []
let mx = 0
let pos = 0
let ml = 0
for (let i = 0; i < str.length; i++) {
if (i < mx) {
rl[i] = Math.min(rl[2 * pos - i], mx - i)
} else {
rl[i] = 1
}
while (i - rl[i] > 0 && i + rl[i] < str.length && str[i - rl[i]] == str[i + rl[i]]) {
rl[i]++
}
if (rl[i] + i - 1 > mx) {
mx = rl[i] + i - 1
pos = i
}
if (ml < rl[i]) {
ml = rl[i]
sub = str.substring(i - rl[i] + 1, i + rl[i])
}
}
return sub.split('#').join('').trim()
}
console.log(longestPalindrome(str)) //输出dabbad
复制代码
该方法的时间复杂度为O(n),效率相对前两种方法有巨大的提高。 有一篇大佬的文章有助于你们对Manacher算法的理解 Manacher算法详解
这三种方法是最长回文子串的最经常使用解法。 暴力解法最容易理解也是最简单,可是算法效率低下。 动态规划对暴力解法作了必定的改进,它避免了在验证回文时进行没必要要的重复计算。 而Manacher算法则是此题效率最高的算法,虽然相对前两种方法稍微难理解一点,可是仔细看看也OK啦😄。 你们若是还有什么更优的解法,欢迎评论区见😄 最后允许小生附上个人github地址 里面记录了我学习前端的点点滴滴,以为有帮助的小哥哥小姐姐能够给个小星星哟😄