1. 程式人生 > >516. 最長迴文子序列

516. 最長迴文子序列

給定一個字串s,找到其中最長的迴文子序列。可以假設s的最大長度為1000

示例 1:
輸入:”bbbab”
輸出:4
一個可能的最長迴文子序列為 “bbbb”,長度為4

示例 2:
輸入:”cbbd”
輸出:2
一個可能的最長迴文子序列為 “bb”,長度為2

注意是子序列而不是子串!
子串是連續的,比如”abda”最長迴文子串就是”a”或者”b”..
子序列是不連續的,比如”abda”最長子序列就是”aba”或者”ada”

解題思路:

aba 因為頭和尾相等,所以最長子序列就是2+中間b的最長子序列=2+1=3
abb 因為頭和尾不相等,所以最長子序列就是ab或者bb的最長子序列=max(ab,bb)=max(1,2)=2
所以我們得到下面的結論
假設非空字串x1x2…xn,如果x1==xn,那麼max=2+max(x2…xn-1);如果不相等的話就是max(x1..xn-1,x2…xn)

(這個思路我是沒想到的,借鑑了其他博主的思路,確實這個思路是很難的,比寫程式碼還難,這個思路沒有的話,很難去解題)

看懂了這個思路,我心急火燎的想到了遞迴

class Solution {
    public int longestPalindromeSubseq(String s) {
             if(s==null||s.length()==0) return 0;
        if(s.length()==1) return 1;

        char[] chars = s.toCharArray();
        if(s.length()==2
) return chars[0] == chars[1] ? 2 : 1; if(chars[0]==chars[s.length()-1]){ return 2 + longestPalindromeSubseq(s.substring(1,s.length()-1)); }else { return Math.max(longestPalindromeSubseq(s.substring(0, s.length() - 1)), longestPalindromeSubseq(s.substring(1, s.length()
))); } } }

但是執行的結果是跑到第61個測試用例的時候超出時間限制,說明這種遞迴太耗時了,畢竟題目故意讓字串變得很大,也說明很多人都能想到遞迴,但是出題者就是不想讓你這麼做!

所以在上述解題思路上我們需要增加動態規劃的思想,一個字元一個字元的增加,求其最大序列,比如a最長子序列是1,ab呢?求出ab再加一個c,abc呢?再求出abc,然後依次加完所有的字元

class Solution:
    def longestPalindromeSubseq(self, s):
        """
        :type s: str
        :rtype: int
        """
        length = len(s)
        if length == 0 or length == 1:
            return length
        if s == s[::-1]:
            return length

        p = [[0] * length for i in range(length)] 

        for j in range(0, length):
            p[j][j] = 1
            for i in reversed(range(0, j)):
                if s[i] == s[j]:
                    p[i][j] = p[i + 1][j - 1] + 2
                else:
                    p[i][j] = max(p[i+1][j], p[i][j-1])


        return p[0][length-1] 

雖然是Python寫的但是沒關係,我們看下他的思路然後吸取思路然後轉成對應的語言即可

我們寫個例子就可以知道其思路:

這裡寫圖片描述

給定一個字串”aba”尋找最大的子序列

水平方向表示a到a所能組成的最大子序列,比如3表示的就是第一個a到第三個a的最大子序列是3,3左邊的1表示的就是從a到b的最大子序列是1
豎直方向我們是從下到上,3表示的是從第三個a到第一個a所能組成的最大子序列長度,3下面的1表示的是從第三個a到b所能組成的最大子序列是1
所以我們最後找到這個二維陣列最右上的那個元素就是最長的子序列個數了

我們從a開始,依次加上b,a兩個字元
a就一個字元,就是1
ab的話,看豎直方向,b本身是一個字元,所以放入1進入陣列,b和第一個a比較的時候發現不一樣,於是找a或者b所能構成的最大子序列,於是max(1,1)=1 也就是ab所能構成的最大子序列就是1

aba的話,豎直方向上,第三個a初始值也是1,第三個a不和b一直,所以取最大仍然是1,第三個a和第一個a比較發現相等,這個時候就需要注意了,首尾字元相等我們的思路是去掉兩個字元剩下的字元所能組成的最大序列,因此是3=左下角的1+2,左下角表示的就是除去兩個字元所能組成的最大子序列!

JAVA程式碼

public int longestPalindromeSubseq(String s) {
        if (s == null || s.length() == 0) return 0;
        if(s.length()==1) return 1;

        char[] chars = s.toCharArray();
        if(s.length()==2) return chars[0] == chars[1] ? 2 : 1;

        //三個字元才開始迴圈判斷
        int[][] array = new int[s.length()][s.length()];
        for (int i = 0; i < s.length(); i++) {
            array[i][i] = 1;
            for (int j = i-1; j >-1 ; j--) {
                if(chars[j]==chars[i]){
                    array[i][j] = 2 + array[i - 1][j + 1];
                }else {
                    array[i][j] = Math.max(array[i-1][j],array[i][j+1]);
                }
            }
        }
        return array[chars.length-1][0];

    }