zoukankan      html  css  js  c++  java
  • leetcode 5 最长回文子串问题

    题目描述

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

    示例 1:

    输入: "babad"
    输出: "bab"
    注意: "aba" 也是一个有效答案。
    示例 2:

    输入: "cbbd"
    输出: "bb"

    方法一暴力求解: 定义一个n*n维的数组flage,flage[i][j] 表示以i开始,以j结束的回文串的长度,如果Si,,,,,,Sj是回文串,那么flage[i][j]就是长度,否则值为0。然后取出flage数组中元素最大的值并返回相应的回文子串。此方法时间复杂度为O(n^3), 空间复杂度可以为O(1),(每次只保存最优结果数据,从而不需要用数组)---复杂度太大会超时

     /**
         * 暴力方法
         * 假设flage[i][j]表示以i开始,以j结束的回文串的长度。
         * 计算出所有以i开始以j结束回文串的长度大小,不是回文串的设置为0
         *
         * @param s
         * @return
         */
        public String longestPalindrome1(String s) {
    
            // a[i][j] 表示以i开始, 以j结束的串, 如果是回文串保存长度,不是直接为0
            int max = 1, x = 0, y = 0;
            int len = s.length();
            int[][] flage = new int[len][len];
            for (int i = 0; i < len; i++) flage[i][i] = 1;
            for (int i = 0; i < len; i++) {
    
                for (int j = i + 1; j < len; j++) {
                    flage[i][j] = TheLengthOfStr(s.substring(i, j + 1));
    
                    if (max < flage[i][j]) {
                        max = flage[i][j];
                        x = i;
                        y = j;
                    }
                }
            }
            return s.substring(x, y + 1);
        }

    方法二:动态规划 本质上是暴力方法的改进版本,

    它利用数组P[i][j]保存Si到Sj是否是回文串,然后利用状态转移方程 P[i][j]={P[i+1][j-1]&&Si==Sj}, 初始化条件为P[i][i]=1, P[i][i+1]=Si==S(i+1)

    先从找两个元素的回文串,再找三个元素的回文串,接着找四个元素的回文串,一直这样下去,直到k个元素的回文串。 时间复杂度为O(n^2), 空间复杂度为O(n^2) 

     public String longestPalindrome2(String s) {
            // 先初始化 一位
            char[] cSubstr = s.toCharArray();
            int maxValue = 0;
            int low = 0, high = 0;
            int len = s.length();
            boolean[][] P = new boolean[len][len];
            for (int i = 0; i < len; i++) {
                P[i][i] = true;
    
            }
            // 从2个元素到k个元素  P[j-k-1][j-1]
            boolean isFlage = false;
            for (int k = 1; k < len; k++) {
                isFlage = false;
                for (int j = k; j < len; j++) {
                    P[j - k][j] = cSubstr[j - k] == cSubstr[j]&&(k==1||P[j-k+1][j-1]);
                    if (P[j - k][j]&&!isFlage) {
                        maxValue = k;
                        low = j - k;
                        high = j;
                        isFlage = true;
                    }
                }
            }
            return s.substring(low, high + 1);
        }

    方法三:中心扩展法,其实就是枚举所有可能的中心点,从而来获得最大的回文串长度。 由于可能存在奇数或者偶数个回文串长度,所以中心点的选择需要考虑上述两种情况。

    当回文串长度为偶数时,有n-1种可能的中心点;当回文串长度为奇数时,存在n种可能的中心点。也就是说总共存在2n-1种中心点情况。时间复杂度为O(n^2)

     public String longestPalindrome3(String s) {
            int len = s.length();
            if(len<=1) return s;
    // 表示最优回文串对应的上下界, 界限包含在内
    int start = 0, end = 0; for (int i = 0; i < len; i++) {
    // 奇数情况
    int len1 = theLengthOfSubStr(s, i, i, len);
    // 偶数情况
    int len2 = theLengthOfSubStr(s, i, i + 1, len); int le = Math.max(len1, len2); if (le > start - end) { // 如果新的回文串长度更长,修改回文串对应的上下界end, start start = i - (le-1 )/ 2; end = i + le / 2; } } return s.substring(start, end + 1); } public int theLengthOfSubStr(String s, int left, int right, int len) { int L = left, R = right; while (L >= 0 && R < len && s.charAt(L) == s.charAt(R)) { R++; L--; } return R - L - 1; }

    方法四:马拉车方法Manacher,  此方法有点和KMP类似, 都是利用已知的一些信息来避免重复计算问题。此种方法时间复杂度为O(n),非常快。

    该方法存在两个小技巧,第一个利用添加特殊字符的方法解决回文子串长度的奇偶数问题。第二点利用特殊字符防止下表越界。

    假设原本输入数据为babad

    第一个技巧是再每一元素的左右两边都加入'#'字符。假设原来数据长度为n, 则‘#’字符出现的个数为n+1, 从而总的数据长度变为奇数2n+1。

        #b#a#b#a#d

    第二个技巧实在边界两端添加特殊字符,防止小标越界(代码中会讨论)

    ?#b#a#b#a#d

    一些定义及概念

    cArray表示字符串数组, p[i]表示以元素cArray[i]为对称中心的最大回文串的半径。 id表示当前最大长度的回文串对应的对称中心,mx当前最大回文串对应的右边界(不包含边界),其左边界为ml。j=2*id-i 表示i以id为对称中心的情况下对应的对称点。我们直到当i<mx时,i对应对称点j的p[j]我们是知道的,从而我们可以利用已知的p[j]来求相应的p[i],j的左边界为jl, 右边界为jr,这样会避免重复计算。

    我们依据条件不同分为三种情况来计算相应的p[j]

    第一种情况, i<mx, j 的回文串有一部分在 id 的之外。 此种情况下i不能继续扩展, p[i]=p[j=2*id-i]

    第二种情况 i<mx, j的回文串有一部分在 id 的之内。 此种情况下i不能继续扩展, p[i]=p[j=2*id-i]

    第三种情况i<mx, j 回文串左端正好与 id 的回文串左端重合,此时p[i] = p[j]p[i] = mx - i,并且p[i]还可以继续增加。

    具体实现代码如下

    /**
         * 利用马拉车方法Menacher
         *
         * @param s 目标字符串
         * @return
         */
        public String longestPalindrome4(String s) {
    
            int len = s.length();
            if(len<=1) return s;
            len = 2*len + 3;
            char cArray[] = new char[len];
            cArray[0] = '$';
            cArray[1] = '#';
            cArray[len-1] = '
    ';
            for (int i = 2, j=0; i <len-1; i=i+2) {
                cArray[i]=s.charAt(j++);
                cArray[i + 1] = '#';
            }
            // p[i]保存以点cArray[i]为对称中心的最大半径长度
            int[] p = new int[len];
            // 表示当前最大半径的中心所在位置, mx当前最大半径所在的有边界, maxLen最大的回文串长度
            int id=0, mx = 0, maxLen = -1, medium=-1;
            for (int i = 1; i < len-1; i++) {
                if(i<mx){//可以借助对称性来求p[i]
                    p[i] = Math.min(p[2 * id - i], mx - i);
                }else
                    p[i] = 1; // 长度为1
                  // 不用判断越界,因为左右两边分别添加'$'和''防止溢出
                while (cArray[i - p[i]] == cArray[i + p[i]]) {
                    p[i]++;
                }
                // 更新当前最远右边界
                if (i + p[i] > mx) {
                    mx = i + p[i];
                    id = i;
                }
                System.out.println("i = " + i + "  p[i] = " + p[i] + "");
                 // p[i] - 1 当前以i为中心的回文串的长度(去掉#)
                if (p[i] - 1 > maxLen) {
                    maxLen = p[i] - 1;
                    medium = i - maxLen;
                }
    
            }
            // 表示当前最优回文串的起始位置
            medium = medium / 2;
    
            return s.substring(medium, medium + maxLen);
        }

     Manacher方法参考 https://ethsonliu.com/2018/04/manacher.html

  • 相关阅读:
    Python3之random模块常用方法
    Go语言学习笔记(九)之数组
    Go语言学习笔记之简单的几个排序
    Go语言学习笔记(八)
    Python3之logging模块
    Go语言学习笔记(六)
    123. Best Time to Buy and Sell Stock III(js)
    122. Best Time to Buy and Sell Stock II(js)
    121. Best Time to Buy and Sell Stock(js)
    120. Triangle(js)
  • 原文地址:https://www.cnblogs.com/09120912zhang/p/12585071.html
Copyright © 2011-2022 走看看