算法-寻找最长回文子串, Manacher算法

题目

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

解题

首先什么是回文?回文就是对称的字符串,正读反读都一样。每个字符都要左右寻找是否对称。因此时间复杂度为O(n2)。 题解中还提到了一种时间复杂度为O(n)的算法,马拉车算法Manacher‘s Algorithm。

首先中心对称算法的实现

/**
 * @param {string} s
 * @return {string}
 */
var longestPalindrome = function(s) {
    let maxRoundStr = "";
    for( let i = 0; i < s.length; i++ ){
        let roundResult = findRoundStr( i, s );
        const resultStr = roundResult.result;
        if( roundResult.offset > 0 ) i+= roundResult.offset;
        if( resultStr.length > maxRoundStr.length ){
            maxRoundStr = resultStr;
            //剩余字符不可能产生比当前更长的回文
            if( (maxRoundStr.length / 2) > s.length - i ) break;
            
        }
    }
    return maxRoundStr;
};

function findRoundStr( position, str ){
    let offsetRight = 1;
    let offsetLeft = 1;
    const startChar = str[position];
    let resultStr = str[position];
    let rightChar = str[position+offsetRight];
    while( rightChar === startChar ){
        resultStr += rightChar;
        offsetRight++;
        rightChar = str[position+offsetRight];
    }
    let offset = offsetRight - 1;
    let preChar = "";
    let nextChar = "";
    while( preChar != undefined && nextChar != undefined && preChar === nextChar ){
        resultStr = preChar + resultStr + nextChar;
        preChar = str[position-offsetLeft];
        nextChar = str[position+offsetRight];
        offsetLeft++;
        offsetRight++;
    }
    return {
        result: resultStr,
        offset: offset  //相同字符跳过
    };
}

Manacher算法实现

/**
 * @param {string} s
 * @return {string}
 */
var longestPalindrome = function(s) {
    //插入字符转换成奇数字符串
    const str = "$#"+s.split("").join("#")+"#$";
    //以各个位置的字符为中心的回文字符半径
    let p = [];
    //id位置的回文最右端位置
    let mx = 0;
    //参考位置id
    let id = 0;
    //回文最大半径
    let maxLen=0;
    //回文最大半径字符串中心位置
    let maxPos=0;
    for( let i = 1; i < str.length; i++ ){
        if( mx > i ){
           p[i] = Math.min( p[2*id-i], mx-i );
        }else{
           p[i] = 1;
        }
        while( str[ i-p[i] ] && str[ i + p[i] ] && str[ i-p[i] ] === str[ i + p[i] ] ) p[i]++;
        if( mx < p[i] + id ){
           id = i;
           mx = p[i] + i;
        }
        if( p[i] > maxLen  ){
           maxPos = i;
           maxLen = p[i];
        }
    }
    return s.substring((maxPos-maxLen)/2, (maxPos+maxLen)/2-1);
};

分享

Author | 何小亮

全栈开发工程师(Node.js,Golang).