算法導論15.2 最長迴文子序列 Longest palindrome subsequence

算法導論15.2 最長迴文子序列,注意其子序列可以是不連續的。

15-2 Longest palindrome subsequence
A palindrome is a nonempty string over some alphabet that reads the same forward
and backward. Examples of palindromes are all strings of length 1, civic, refer,
racecar, and aibohphobia (fear of palindromes).

Give an efficient algorithm to find the longest palindrome that is a subsequence
of a given input string. For example, given the input character, your algorithm
should return carac. 

主要有三種算法:

1. 把當前串A逆序轉換爲另外一個串B,把問題轉換爲求這兩個串的最長公共子序列問題,O(n^2)

2.直接用動態規劃方法求此字符串的最長迴文子序列,O(n^2)

3.使用Manacher算法求最長迴文序列,O(n)迴文子串算法,但Manacher算法只能計算出下標連續的迴文子串,例如characher返回最大字串長度爲3(ara),而不是5(carac)。

參考了網上的資料,第二種方法大多給出了長度,沒有給出最長迴文串,下面是我寫的方法2的代碼。

  public static void main(String[] args) {
    longestPalindromeSubseq("cib");       //c
    longestPalindromeSubseq("biic");      //ii
    longestPalindromeSubseq("civic");      //civic
    longestPalindromeSubseq2("beneab");    //beneb
    longestPalindromeSubseq("character"); //carac
    longestPalindromeSubseq("abacdfgdcaba"); //abacdfdcaba
  }

/*
設字符串爲s,f(i,j)表示s[i..j]的最長迴文子序列。 最長迴文子序列長度爲f(0, s.length()-1)
狀態轉移方程如下:
當i>j時,f(i,j)=0。 
當i=j時,f(i,j)=1。 
當i<j並且s[i]=s[j]時,f(i,j)=f(i+1,j-1)+2。 
當i<j並且s[i]≠s[j]時,f(i,j)=max( f(i,j-1), f(i+1,j) )。 
注意如果i+1=j並且s[i]=s[j]時,f(i,j)=f(i+1,j-1)+2=f(j,j-1)+2=2,這就是當i>j時f(i,j)=0的好處。
 */
  public static String longestPalindromeSubseq(String str) { // O(n^2)
    if(str == null || str.length()<=1) {
      return str;
    }
    System.out.println(Arrays.toString(str.toCharArray()));
    int n = str.length();
    String[] resultStrs = new String[n]; //index+1 is palindrome subseq with length index+1
    resultStrs[0] = "" + str.charAt(0); // initialize to first char for length is 1

    int[][] matrix = new int[n][n]; //default value is 0 

    for(int j=0; j<n-1; j++) {
      matrix[j][j] = 1;     // f(j,j)=1; f(j,j-1)=0, same as default value
    }
    int startIndex = 0;
    int endIndex = 0;
    int maxLen = 1;
    for(int i=n-1; i>=0; i--) {
      for (int j = i+1; j < n; j++) {
        // i<j case
          if (str.charAt(i) == str.charAt(j)) {
            int preLen = matrix[i + 1][j - 1];
            matrix[i][j] = 2 + preLen;
            if (maxLen <= matrix[i][j]) {
              maxLen = matrix[i][j];
              StringBuilder sb = new StringBuilder("");
              if(preLen==1) {
                sb.append(str.charAt(i));
                sb.append(str.charAt(i+1)); // or j-1
                sb.append(str.charAt(j));
              }
              else if(preLen==0) {
                sb.append(str.charAt(i));
                sb.append(str.charAt(j));
              }
              else {
                sb.append(str.charAt(i));
                sb.append(resultStrs[preLen-1]);
                sb.append(str.charAt(j));
              }
              resultStrs[maxLen-1] = sb.toString();
              startIndex = i;
              endIndex = j;
            }
          }
          else {
            matrix[i][j] = Math.max(matrix[i + 1][j], matrix[i][j - 1]);
          }
      }
    }
    System.out.println("startIndex:"+startIndex + ", endIndex:"+endIndex);
    System.out.println("max length:"+maxLen);
    System.out.println("sub string:" + str.substring(startIndex, endIndex+1));
    String longestSubSeq = null;
    for (int i = 0; i < n; i++) {
      if (resultStrs[i] != null && resultStrs[i].length() == maxLen) {
        longestSubSeq = resultStrs[i]; // find the longest from n-1
        break;
      }
    }
    System.out.println("longest palindrome subseq:" + longestSubSeq);
    return longestSubSeq;
  }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章