LeetCode 5.Longest Palindromic Substring

Leetcode : LongestPalindromicSubString
Diffculty:Medium

求最长回文子串。
这是一个在面试中比较常出现的算法题。最优解法是Manacher算法,实际上用的是动态规划的思路,首先通过增加间隔符,将结果变为找aba类型的回文串,然后利用已经找到的回文串结果,逐个向后查找更长的回文串。只需遍历一遍字符串即可。

题目

给一个字符串,找出其中最长的回文子串。
注意 aba 和 abba 都属于回文字符串。

目前有两种解法:
1)Manacher算法:时间复杂度O(N)
2)中心点扩散法:时间复杂度O(N^2)

Manacher算法参考思路:https://segmentfault.com/a/1190000003914228


解法

Manacher算法

   /**
     * Manacher 算法
     * 充分利用回文特性,将字符中间插入 # 将单双回文统一转化为单回文问题
     * 然后维护一个数组p,该数组保存以 i 点为中心,最长的回文半径。
     * 那么p[i]-1 就是原字符中,以i为中心的回文长度
     * 那么问题的结果就是 max(p[i]) - 1
     *
     * 重点在于计算数组p
     *
     * 时间复杂度:O(N)
     */
    public static String manacher(String str){
        char[] t = preprocess(str);
        int[] p = new int[t.length];    // p[i] 表示以p为中心的最长的回文半径


        int mid = 0;        // 已触及的最右边的回文串 在p数组中的 索引 即 p[i] 的 i
        int maxRight = 0;   // 已触及的最右边的回文串,即 p[i]的值


        int length = 0; // 存储p数组中最大的值
        int center = 0; // 该最大值的中心index

        for(int i=1; i<t.length-1; i++){
            int mirror = 2*mid -i;  // i相对于mid的对称镜像位置
            if(i < maxRight){
                // i 在maxRight的左边
                // 则p[i] 可以暂时赋值为 镜像位置和maxRight-1 之间的最小值
                // 如果p[mirror] 小于maxRight-i 那么 p[i] 肯定是等于 p[mirror]
                // 如果p[mirror] 大于maxRight-i 那么 p[i] 至少等于 maxRight-i 由于 maxRight之后还没有比较过,需要从maxRight+1开始关于i对称进行比较。

                if(p[mirror] < maxRight-i){ // 注意 这里必须是小于。只有小于边界才能确定 p[i]=p[mirror] 等于只能确定至少是这样
                    p[i] = p[mirror];
                    // 已得到p[i] 结果,与length比较
                    if(p[i] > length){
                        length = p[i];
                        center = i;
                    }
                    continue;
                }else{
                    p[i] = maxRight-i;
                }
            }

            // p[i] 向两边扩散,相等则值+1。直到找到不等的为止
            while(t[i+(1+p[i])] == t[i-(1+p[i])]){
                p[i]++;
            }

            // d得到最终p[i]的值以后
            // 如果 maxRight 在 i+p[i] 左边
            // 则maxRight=i+p[i] 顺便将回文中点置为i 即 mid=i;
            if(i+p[i] > maxRight){
                mid = i;
                maxRight = i + p[i];
            }

            // 把当次p[i] 计算结果与 最长结果比较并替换
            if(p[i] > length){
                length = p[i];
                center = i;
            }
        }
        return str.substring((center-1-length)/2, (center-1+length)/2);
    }


    // 预处理 处理成 $#a#b#c#d#e#@ 的形式
    // 头部$ 尾部@ 然后用# 隔开每一个元素
    // 由于回文字串会有奇数串和偶数串
    // 转为为 #a#b#a# 或者 #a#b#b#a# 以后,长度统一为了奇数个
    private static char[] preprocess(String str){
        char[] t = new char[str.length()*2 + 3];
        t[0] = '$';                 //起始边界
        t[str.length()*2 + 2] = '@';  //结束边界
        for(int i=0; i<str.length(); i++){
            t[2*i +1] = '#';
            t[2*i +2] = str.charAt(i);
        }
        t[str.length()*2 + 1] = '#';    //补上最后一个#

        return t;
    }



中心点扩散法

//回文子串结果
    static int lIndex = 0;
    static int rIndex = 0;
    static int maxLength = 0;


    /**
     * 中心点扩散法, 通过了leetcode
     * @param str
     * @return
     */
    public static String longestPalindrome(String str){
        if(str == null || str.length() <=2){
            return str;
        }

        for(int i=0; i<str.length(); i++){
            searchPalind(str, i, i);
            searchPalind(str, i, i+1);
        }
        // 最后仅执行一次substring
        return str.substring(lIndex, rIndex);
    }

    /**
     * 从中间向两边找回文字串在 str 中的最大索引范围。
     * @param str
     * @param low
     * @param high
     */
    private static void searchPalind(String str, int low, int high){
        while(low>=0 && high<str.length()){
            if(str.charAt(low) == str.charAt(high)){
                if(high-low+1 > maxLength){
                    lIndex = low;
                    rIndex = high+1;
                    maxLength = high - low + 1;
                }
            }else{
                // 如果不等于就退出
                return;
            }
            low--;high++;
        }
        return;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351