常考数据结构与算法:最长回文子串_子串

 

 

暴力解法:

   列举所有的子串,判断是否为回文串,保存最长的回文串。

 /*
    * 暴力解法: 列举所有的子串,判断是否为回文串,保存最长的回文串。
    */
    public int getLongestPalindrome(String A, int n) {
        if(n == 0 || n == 1)
            return n;

        int count = 0;
        String temp;
        for (int i = 0; i < n; i++) {
            for (int j = i+1; j <= n; j++) {
                // 截取字符串
                temp = A.substring(i,j);
                // 判断字符串是否为回文
                if(isPalindrome(temp) && (j-i) > count){
                    count = j-i;
                }
            }
        }
        return count;
    }

    private boolean isPalindrome(String str){
        int start=0;
        int end=str.length()-1;

        while(start<end){
            if(str.charAt(start) == str.charAt(end)){
                start++;
                end--;
            }else{
                return false;
            }
        }

        return true;
    }

 

动态规划(利用已有的信息,判断下一步)

  暴力解法时间复杂度太高,我们可以考虑,去掉一些暴力解法中重复的判断。

  首先定义:字符串 s 从下标 i 到下标 j 的字串为 P(i, j),若 s 从下标 i 到下标 j 的字串为回文串,则 P(i, j) = true,否则 P(i, j) = false。如下图所示:

常考数据结构与算法:最长回文子串_回文串_02

  则 P(i, j) = (P(i + 1, j - 1) && s[i] == s[j])。

  所以如果我们想知道 P(i,j)的情况,不需要调用判断回文串的函数了,只需要知道 P(i+1,j−1)的情况就可以了,这样时间复杂度就少了 O(n)。因此我们可以用动态规划的方法,空间换时间,把已经求出的 P(i,j)存储起来。

常考数据结构与算法:最长回文子串_暴力解法_03

  如果 s[i+1, j-1] 是回文串,那么只要 s[i] == s[j],就可以确定 s[i, j] 也是回文串了。

注意:

  求 长度为 1 和长度为 2 的 P(i, j) 时不能用上边的公式,因为我们代入公式后会遇到 P[i][j] 中 i > j 的情况,比如求P[1][2] 的话,我们需要知道 P[1+1][2-1]=P[2][1] ,而 P[2][1] 代表着 S[2, 1] 是不是回文串,这显然是不对的,所以我们需要单独判断。

  所以我们先初始化长度是 1 的回文串的 P [i , j],这样利用上边提出的公式 P(i,j)=(P(i+1,j-1) && S[i]==S[j]),然后两边向外各扩充一个字符,长度为 3 的,为 5 的,所有奇数长度的就都求出来了。同理,初始化长度是 2 的回文串 P[i,i+1],利用公式,长度为 4 的,6 的所有偶数长度的就都求出来了。
 

常考数据结构与算法:最长回文子串_字符串_04

 

 public int getLongestPalindrome(String A, int n) {
        int sLen = n;
        int maxLen = 0;
        String ans = "";
        boolean[][] P = new boolean[sLen][sLen];
        // 遍历所有长度  "abc1234321"
        for (int len = 1; len <= sLen; len++) {
            for (int start = 0; start < sLen; start++) {
                int end = start + len - 1;
                // 下标越界,结束循环
                if (end >=sLen) {
                    break;
                }
                P[start][end] = (len == 1 || len == 2 || P[start + 1][end - 1]) && A.charAt(start) == A.charAt(end);
                if (P[start][end] && len > maxLen) {
                    maxLen = len;
                    ans = A.substring(start, end + 1);
                }
            }
        }
        return ans.length();
    }