算法导论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;
  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值