【力扣刷题记5】——《最长回文子串》

913 阅读1分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

一、题目描述:

  1. 最长回文子串-难度中等

给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:

输入:s = "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。 示例 2:

输入:s = "cbbd" 输出:"bb"  

提示:

1 <= s.length <= 1000

s 仅由数字和英文字母组成

二、题目和思路分析:

看到这个问题,我的第一反应是:回文字符串,即正反看起来都一样的字符串,这道题和上次做的寻找最长子串的问题很类似。

所以只要找最长子串的同时,判断该子串是否是回文字符串即可。

想着比较简单,当我按着上次做的寻找最长子串的做法再来一次的时候,来来回回报了好几个错,i和j写错了,声明的j写成了i,或者没考虑一个字符的情况,或者判断非回文的情况赋值错了。总之就是过于粗心大意,考虑不周到。

最后这些小毛病总算全部改完了,却发现提交提示【超出时间限制】,本地测了一下,很长的字符串要三秒多钟,看来暴力破解实在是走不通啊!!!

说起来也是,算法题理应尽可能地解出最优解,虽然我前面几道题也几乎都是暴力破解的。

那么是不是还有其他方式来判断回文呢?

从中位数分割字符串,这样只要判断分割的前一半翻转值是否等于分割的后一半即可,从翻转对比的角度来说,少了一半的运算量,这样总能行了吧!

然而,我还是考虑不周,字符的长度没有考虑进去,截取的下标误减去1,导致提交得到的结果仍然是错误的。最终改完这些小错误,却发现提交还是提示【超出时间限制】。

啊啊啊啊,这道题怎么这么麻烦!!!

于是我开始反思,双重的for循环可能是不对的。还有没有其它方法呢?

这时候我想到,如果只是遍历当前字符字符串,默认值初始取第一个字符,判断当前遍历的字符前后两项是否相等,如果相等则赋给默认值,继续判断默认值的前后两项,如此循环即可。

这么想着,我写出了代码,结果发现,当回文字符串长度为偶数时,结果是错误的。

因为以遍历字符的形式,判断不出偶数形式的回文数,也就是要根据偶数的回文数,即回文中间的部分是否相等来添加判断。

这一版终于改成功了~

是不是还要优化的空间呢?我想了又想,如果当前的子串长度超过字符串的一半,还不是回文子串,那么就没必要再对该子串进行累加了。

所以我又改了一版,结果是:错误的!!!

也就是说整个字符串都可能是回文字符串,所以没有做额外判断的时候,一定要判断到最后一位才可以。

三、代码:

代码实现如下:

/**
 * @param {string} s
 * @return {string}
 */
// 第一版
// var longestPalindrome = function(s) {
//     let ss = '' // 默认最长回文子串
//     if (!s) return ''
//     if (s.length == 1) return s
//     let length, s2, s3, a
//     for(let i = 0; i < s.length; i++){
//         let s1 = s[i]  // 初始值
//         !ss? ss = s1 : ss
//         for(let j = i+1; j < s.length; j++){
//             s1 += s[j]
//             // 回文的判断方法修改
//             // 取数组
//             length = s1.length
//             if(length == 2){
//                 s1[0] == s1[1] && ss.length < length && (ss = s1)
//             }else{
//                 if(length % 2 == 1){ // 是奇数
//                     a = (length-1)/2 // 中位数
//                     s2 = s1.slice(0, a)
//                     s3 = s1.slice(a+1)
//                 }else{
//                     a = length/2
//                     s2 = s1.slice(0, a)
//                     s3 = s1.slice(a)
//                 }
//                 if([...s2].reverse().join("") == s3){
//                     ss.length < length? ss = s1 : ss
//                 }else{
//                     if(s1.length > (s.length/2)){
//                         break
//                     }
//                 }
//             }
//         } 
//     }//     if(s.length)
//     return ss// };
​
​
​
​
var longestPalindrome = function(s) {
    let ss = '', s1 = ''
    for(let i = 0; i<s.length; i++){
        // console.log(`当前是for循环的第${i}次`)
        if(s[i] == s[i+1]){
            s1 = s[i] + s[i]
            let uu = i-1
            let oo = i+2
            while(s[uu] !== void(0) && s[oo] !== void(0) && s[uu] === s[oo]){
                // console.log(`当前while循环,u是${uu},o是${oo}`)
                s1 = s[uu] + s1 + s[oo]
                --uu
                ++oo
            }
        }
        let u = i-1
        let o = i+1
        s1 = s[i]
        while(s[u] !== void(0) && s[o] !== void(0) && s[u] === s[o]){
            // console.log(`当前while循环,u是${u},o是${o}`)
            s1 = s[u] + s1 + s[o]
            --u
            ++o
        }
        ss.length < s1.length? ss = s1 : ss
        // console.log(`第${i}次循环,s1=${s1},ss=${ss}`)
    }
    return ss
}

四、总结:

今天这道题目费了很长时间,原先看到题目的时候,我觉得特别简单,却没想到在这花了几个小时的时间,最开始是想的思路错了,做了双重for循环,后面是思路对了,但是判断条件弯弯绕绕,整体把握有失误。

有了正确的思路,还要快速写出正确的代码,加油吧!

加油吧!

image.png