From 3ddd1a277b3c18cce0f2b687551da406f9a2a1ce Mon Sep 17 00:00:00 2001 From: jerryfishcode <91447694+jerryfishcode@users.noreply.github.com> Date: Mon, 27 Sep 2021 22:53:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A05.=20=E6=9C=80=E9=95=BF?= =?UTF-8?q?=E5=9B=9E=E6=96=87=E5=AD=90=E4=B8=B2=20JavaScript=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0005.最长回文子串.md | 111 ++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/problems/0005.最长回文子串.md b/problems/0005.最长回文子串.md index c78b827c..5897495f 100644 --- a/problems/0005.最长回文子串.md +++ b/problems/0005.最长回文子串.md @@ -297,6 +297,117 @@ class Solution: ## JavaScript ```js +//动态规划解法 +var longestPalindrome = function(s) { + const len = s.length; + // 布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false + let dp = new Array(len).fill(false).map(() => new Array(len).fill(false)); + // left起始位置 maxlenth回文串长度 + let left = 0, maxlenth = 0; + for(let i = len - 1; i >= 0; i--){ + for(let j = i; j < len; j++){ + // 情况一:下标i 与 j相同,同一个字符例如a,当然是回文子串 j - i == 0 + // 情况二:下标i 与 j相差为1,例如aa,也是文子串 j - i == 1 + // 情况一和情况二 可以合并为 j - i <= 1 + // 情况三:下标:i 与 j相差大于1的时候,例如cabac,此时s[i]与s[j]已经相同了,我们看i到j区间是不是回文子串就看aba是不是回文就可以了,那么aba的区间就是 i+1 与 j-1区间,这个区间是不是回文就看dp[i + 1][j - 1]===true + if(s[i] === s[j] && (j - i <= 1 || dp[i + 1][j - 1])){ + dp[i][j] = true; + } + // 只要 dp[i][j] == true 成立,就表示子串 s[i..j] 是回文,此时记录回文长度和起始位置 + if(dp[i][j] && j - i + 1 > maxlenth) { + maxlenth = j - i + 1; // 回文串长度 + left = i; // 起始位置 + } + } + } + return s.substr(left, maxlenth); // 找到子串 +}; + +//双指针 +var longestPalindrome = function(s) { + let left = 0, right = 0, maxLength = 0; + const extend = (s, i, j, n) => {// s为字符串 i,j为双指针 n为字符串长度 + while(i >= 0 && j < n && s[i] === s[j]){ + if(j - i + 1 > maxLength){ + left = i; // 更新开始位置 + right = j; // 更新结尾位置 + maxLength = j - i + 1; // 更新子串最大长度 + } + // 指针移动 + i--; + j++; + } + } + for(let i = 0; i < s.length; i++){ + extend(s, i, i, s.length); // 以i为中心 + extend(s, i, i + 1, s.length); // 以i和i+1为中心 + } + return s.substr(left, maxLength); +}; + +//Manacher算法 +var longestPalindrome = function(s) { + const len = s.length; + if(len < 2) return s; + let maxLength = 1, index = 0; + //Manacher算法,利用回文对称的性质,根据i在上一个回文中心的臂长里的位置去判断i的回文性 + //需要知道上一个回文中心,以及其臂长 + let center = 0; + //注意这里使用了maxRight的而不是真实的臂长length,因为之后需要判断i在臂长的什么位置 + //如果这里臂长用了length,之后还要 计算i - center 去和 length比较,太繁琐 + let maxRight = 0; + //考虑到回文串的长度是偶数的情况,所以这里预处理一下字符串,每个字符间插入特殊字符,把可能性都化为奇数 + //这个处理把回文串长度的可能性都化为了奇数 + //#c#b#b#a# + //#c#b#a#b#d# + let ss = ""; + for(let i = 0; i < s.length; i++){ + ss += "#"+s[i]; + } + ss += "#"; + //需要维护一个每个位置臂长的信息数组positionLength + const pl = new Array(ss.length).fill(0); + //这里需要注意参考的是i关于center对称的点i'的回文性 + //i' = 2*center - i; + //所以列下情况: + //1.i>maxRight,找不到i',无法参考,自己算自己的 + //2.i<=maxRight: + //2.1 i= pl[i‘],大多少需要尝试扩散 + //2.3 i>maxRight-pl[i'],pl[i']的臂长超过了center的臂长,根据对称性,i中心扩散到MaxRight处, + // s[2*i-maxRight] !== s[MaxRight]必不相等,所以pl[i] = maxRight-i; + //总结就是pl[i] = Math.min(maxRight-i,pl[i']);提示i= 0 && right maxRight){ + center = i; + maxRight = pl[i] + i; + } + if (pl[i] * 2 + 1 > maxLength){ + maxLength = pl[i]*2+1; + index = i - pl[i]; + } + } + return ss.substr(index, maxLength).replace(/#/g,""); +}; ``` -----------------------