(最长连续回文串---一个更容易想到的算法)Longest Palindromic Substring

关于这道题,我看了国内一些帖子,基本上都是从国外的帖子翻译的,大概有3种算法,动态规划(O(N2)),KMP匹配最长前缀(O(N2))和一个叫做Manacher(O(N))的算法。前两种算法可能比较容易想到,但是不管是复杂度还是编码难度,都不是一个优雅的选择,而第三种算法是一个非普适的算法,如果不事先知道,是几乎不可能在面试中想出来的,而且我认为在最坏情况下其复杂度也不是O(N)。

在这里我提供我的解法,效率不比Manacher差,复杂度为O(kn - k2),其中k为解的长度,在最坏情况下复杂度为O(N2),最好情况下为O(N)。

先贴上我的运行结果
(最长连续回文串---一个更容易想到的算法)Longest Palindromic Substring_第1张图片
还是比较理想的。

下面说下思路。
我的思路是
1、以整个串的中心为回文串的中心
2、①如果当前中心可以得到的最长回文串小于当前最大值,当前最大值就是答案,结束。②否则,从中心开始向两边迭代比较对称位置上的字符是否相等,记录当前最大值及开始和结束位置索引。③如果迭代结束也没有碰到不相等的字符,说明以当前中心为中心的串就是答案,结束。如果碰到不相等的字符,记录当前最大值和位置信息。
3、将中心位置分别向左、右移动半位(因为中心位置可能不是一个字符而在两个字符之间),跳到第2步。

解释一下,因为题目要求解最长子串,所以不必计算出所有中心的回文串,只要找到最大值就停止。但是如何判断找到最大值呢?考虑一个字符串,可能的最大回文子串就是它自身,以它自身的中点为中点,而这个中点每向左(右)移动半位,新中点可能的最大回文串长度为移动之前的长度减一。
(最长连续回文串---一个更容易想到的算法)Longest Palindromic Substring_第2张图片
图中黑线为整个字符串的中心,此时可能得到的最长子串的长度就是字符串的长度10。蓝线为向两边移动半位,可能得到的最长子串长度变为9(A到F、B到E),红线为再移动半位,可能得到的最长子串变为8(A到E、C到E)。

所以,我们从整个字符串的中点开始向两边扩展,如果在比较某个中点的对称位置时越过了边界,那么说明这就是最长子串,因为在这之后的所有中点可能的最长子串都不会超过这个值,所以最长子串只可能存在于之前已经比较过的位置,而如果之前有一个更长的子串,那么它一定在之前中点的比较中就已经越过边界并返回了。上图中,以D为中点发现所有边界内的对称位置都相等,那么答案就是7,若中点左移半位,能得到的最长子串也只有6了。而如果一直没有越界,由于在每次比较完毕后都会更新当前最大值,如果在比较某个中点时,发现当前可能的最大值小于当前已记录的最大值,那么当前已记录的最大值就是答案,因为在之后所有的比较中都不会有一个更长的串了。

贴上代码:

public String longestPalindrome(String s) {
        char[] input = s.toCharArray();
        int length = input.length;
        int max = 0, left = 0, right = length - 1; // 当前最长回文串的长度和起始、结束位置
        boolean odd; // 当前中点为一个字符或在两个字符之间
        /**
         * odd 初始化
         */
        if (length % 2 == 0) {
            odd = false;
        } else {
            odd = true;
        }
        for (int il = length / 2, ir; il >= 0;) {
            int sl, sr; // 回文串中应该相等的两个字符的索引
            ir = length - 1 - il; // 右边中点
            int temp = 0; // 以i为中心的串的最大回文数
            if (odd) {
                sl = il - 1;
                sr = il + 1;
                temp = 1;   //如果是奇数,中点为一个字符,temp初始值为1
            } else {
                sl = il - 1;
                sr = il;
                --il; // 如果是偶数,将il左移一位
            }
            if ((sl + 1) * 2 <= max) {
                return s.substring(left, right + 1); // 如果当前可能的最大值已不可能大于已知的最大值,返回
            }
            // 计算temp
            while (true) {
                if (input[sl] == input[sr]) {
                    temp += 2;
                    --sl;
                    ++sr;
                } else {
                    if(temp > max){ // 如果不相等,更新最大值和相应位置索引
                        max = temp;
                        left = sl + 1;
                        right = sr - 1;
                    }
                    break;
                }
                if (sl == -1 || sr == length) {
                    return s.substring(sl + 1, sr); // 如果sl或sr碰到边界,说明已找到答案,返回
                }
            }
            if (odd) {
                sl = ir - 1;
                sr = ir + 1;
                temp = 1;
            } else {
                sl = ir;
                sr = ir + 1;
                temp = 0;
                ++ir; // 如果是偶数,将ir右移一位
            }
            // 计算temp
            while (true) {
                if (input[sl] == input[sr]) {
                    temp += 2;
                    --sl;
                    ++sr;
                } else {
                    if(temp > max){ // 如果不相等,更新最大值和相应位置索引
                        max = temp;
                        left = sl + 1;
                        right = sr - 1;
                    }
                    break;
                }
                if (sl == -1 || sr == length) {
                    return s.substring(sl + 1, sr); // 如果sl或sr碰到边界,说明已找到答案,返回
                }
            }
            // 反转odd
            odd = !odd;
        }
        return "";
    }

你可能感兴趣的:(leetcode)