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