前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >异名解题: 最长回文子串

异名解题: 最长回文子串

作者头像
异名
发布2020-06-08 18:54:22
5450
发布2020-06-08 18:54:22
举报
文章被收录于专栏:异名

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

测试用例一 输入: "babad" 输出: "bab" ("aba" 也是一个有效答案) 测试用例二 输入: "cbbd" 输出: "bb"

解题思路

方法一O(n³): 暴力遍历

N个符合条件的字符串,等于前N-1个字符串加上第N个字符然后取反,那么这个字符串就是回文字符串。它只能找出从头开始的字符,所以还得在外层添加一个循环,更改字符串的起点。以下就是该解法写出来的函数,递归部分复杂度是O(n²),然后外层再套一个N层的遍历,所以它的复杂度是O(n³)。

代码语言:javascript
复制
function longestPalindrome(str) {
    let idx = 0;
    let startIdx = 0;
    let len = str.length;
    let maxSubStr = str[0];
    let curStr = '';
    if (len <= 1) return str;

    while(startIdx < len - 1) {
        curStr = str[startIdx]
        idx = startIdx
        function loop() {
            while (idx < len - 1) {
                idx++;
                curStr = curStr + str[idx]
                if (curStr.split('').reverse().join('') == curStr && curStr.length >= maxSubStr.length) {
                    maxSubStr = curStr;
                }
                loop()
            }
        }
        loop()
        startIdx+=1;
    }

    return maxSubStr
}

但是在LeetCode上提交的时候,第41个用例超时了。该用例的长度为877,我本地在不限时间地跑该用例的耗时是3569.156ms,最长回文子串为fklkf;总结一下问题主要是由于递归解法的效率比较低,函数重复嵌套调用,而且并没有提炼出相同的子问题

方法二O(n²):动态规划/中心扩展

方法一执行超时之后换种指导思想,往动态规划方向想,想到的第一个状态转移方程是F(n+1) == str[n-1]+F(n)+str[n+1] 且满足 str[n+1] == str[n-1]。它刚好可以用递推来实现,因为每个单独的字母都是一个符合条件的答案,然后往左右递增扩展,如果左右相同,那就能够得出下一个回文,直到找到最长的回文。但是这样解的话还要区分两种情况,如果回文的长度是基数,比如cabad的输出是aba,它的中心位是一个字母,但是如果回文的长度是偶数,比如abbc的输出是bb,那它的中心位就是两个字母,所以使用这个方法得分别对这两种情况做处理,下面的代码就是基于这种解法而来,官方解法更简洁

代码语言:javascript
复制
function longestPalindrome(str) {
    if (str.length < 2) return str;
  
    let maxStr = str[0];
    for (let curIdx = 0; curIdx < str.length - 1; curIdx++) {
      let str1 = search(str, curIdx, true);
      let str2 = search(str, curIdx, false);
      let result = str1.length > str2.length ? str1 : str2;
      maxStr = result.length > maxStr.length ? result : maxStr;
    }
    return maxStr;
  }
  
  function search(str, curIdx, isEven) {
    let prevIdx, nextIdx;
    let result = str[curIdx];
  
    if (isEven) {
      if (str[curIdx] == str[curIdx + 1]) {
        result += str[curIdx + 1];
      } else {
        return result;
      }
    }
  
    let maxLoopTime = Math.min(str.length - curIdx, curIdx);
    for (let n = 1; n <= maxLoopTime; n++) {
      prevIdx = curIdx - n;
      nextIdx = isEven ? curIdx + n + 1 : curIdx + n;
      if (str[prevIdx] != str[nextIdx] || str[prevIdx] == undefined) break;
      result = str[prevIdx] + result + str[nextIdx];
    }
  
    return result;
  }

方法二可以改良,上面的逻辑针对回文中心是一个字母的和两个字母两种情况分别做了处理,如果取巧一点,往字符串前后,以及每个字母之间插入一个#,就能够把回文中心是两个字母的情况给去掉,比如cabad插入后变成#c#a#b#a#d#,它的输出是#a#b#a#,回文中心还是字母;abbc插入后变成#a#b#b#c#,它的输出是#b#b#,回文中心变成了#也是一个字母,在这个基础上使用中心扩展就不用考虑两种情况了,这其实也是马拉车算法的第一步。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 异名 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 解题思路
    • 方法一O(n³): 暴力遍历
      • 方法二O(n²):动态规划/中心扩展
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档