動態規劃實例(十二):最長迴文子串(LPS)

     給一個字符串,找出它的最長的迴文子序列的長度。例如,如果給定的序列是“BBABCBCAB”,則輸出應該是7,“BABCBAB”是在它的最長迴文子序列。 “BBBBB”和“BBCBB”也都是該字符串的迴文子序列,但不是最長的。注意和最長迴文子串的區別(參考:最長迴文串)!這裏說的子序列,類似最長公共子序列LCS( Longest Common Subsequence)問題,可以是不連續的。這就是LPS(Longest Palindromic Subsequence)問題。
    最直接的解決方法是:生成給定字符串的所有子序列,並找出最長的迴文序列,這個方法的複雜度是指數級的。下面來分析怎麼用動態規劃解決。
    1)最優子結構
        假設 X[0 ... n-1]  是給定的序列,長度爲n.  讓 L(0,n-1) 表示 序列 X[0 ... n-1] 的最長迴文子序列的長度。
        1. 如果X的最後一個元素和第一個元素是相同的,這時:L(0, n-1) = L(1, n-2) + 2 ,  還以 “BBABCBCAB” 爲例,第一個和最後一個相同,因此 L(1,n-2) 就表示藍色的部分。

        2. 如果不相同:L(0, n-1) = MAX ( L(1, n-1) ,  L(0, n-2) )。 以”BABCBCA” 爲例,L(1,n-1)即爲去掉第一個元素的子序列,L(0, n-2)爲去掉最後一個元素。

    2)重複子問題

具體實例及實現代碼如下所示:

/**
 * @Title: LPS.java
 * @Package dynamicprogramming
 * @Description: TODO
 * @author peidong
 * @date 2017-6-12 上午9:36:55 
 * @version V1.0
 */
package dynamicprogramming;

/**
 * @ClassName: LPS
 * @Description: 最長迴文子序列
 * @date 2017-6-12 上午9:36:55 
 *
 */
public class LPS {

    /**
     *
     * @Title: lpsRecursion
     * @Description: 利用遞歸求解LPS
     * @param seq 字符串
     * @param i  起始位置
     * @param j  終止位置
     * @return
     * @return int
     * @throws
     */
    public static int lpsRecursion(String seq, int i, int j){
        //邊界條件,只有一個元素
        if(i == j)
            return 1;
        if(i > j)
            return 0; //只計算序列seq[i...j]
        //如果首尾相同
        if(seq.charAt(i) == seq.charAt(j))
            return lpsRecursion(seq, i+1, j-1) + 2;

        //首尾不同
        return Math.max(lpsRecursion(seq, i, j-1), lpsRecursion(seq, i+1, j));
    }

    /**
     *
     * @Title: lps
     * @Description:利用動態規劃求解LPS
     * @param seq
     * @param n
     * @return
     * @return int
     * @throws
     */
    public static int lps(String seq, int n){
        //創建狀態轉移矩陣
        int[][] tc = new int[n][n];
        int temp;
        //初始化狀態轉移矩陣
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                tc[i][j] = 0;
            }
        }
        for(int i = 0; i < n; i++)
            tc[i][i] = 1;
        //構建狀態轉移矩陣
        for(int i = 1; i < n; i++){
            temp = 0;
            //考慮所有連續的長度爲i+1 的子串
            for(int j = 0; j + i < n; j++){
                //如果首尾相同
                if(seq.charAt(j) == seq.charAt(j+i))
                    temp = tc[j+1][j+i-1]+2;
                else
                    temp = Math.max(tc[j+1][j+i], tc[j][j+i-1]);
                tc[j][j+i] = temp;
            }
        }
        return tc[0][n-1];
    }
    /**
     * @Title: main
     * @Description: TODO
     * @param args
     * @return void
     * @throws
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        String seq = "acmerandacm";
        int n = seq.length();
        System.out.println("利用遞歸求解LPS結果:"+ lpsRecursion(seq, 0, n-1));
        System.out.println("利用dp求解LPS結果:"+ lps(seq, n));
    }

}


發佈了110 篇原創文章 · 獲贊 65 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章