一、题目
二、题解
2.1 方法一 :求该字符串与它的逆序字符串的最长公共子序列
一个字符串的最长回文序列就是:
该字符串与它的逆序字符串的最长公共子序列
因为我们之前写过求两个字符串的最长公共子序列的题目
具体可见:
2.2 方法二:暴力递归
应用范围尝试模型解题(讨论一个范围的开头和结尾的可能性)
递归函数:
递归函数的含义:只考虑字符串str,从L到R位置的最长回文子序列的长度
终止条件:
L== R时,只剩下一个字符,一个字符是回文序列,长度是1,return 1; L==
R-1时,说明只剩下两个字符,如果这两个字符串相等,则构成回文序列,长度是2,如果不相等,最长的回文子序列长度就是1;
普遍情况:
考虑str[L…R]这个范围上最长回文子序列的可能性 (1)最长回文子序列既不以L开头,也不以R结尾
(2)最长回文子序列以L开头,不以R结尾 (3)最长回文子序列不以L开头,但以R结尾 (4)最长回文子序列以L开头,以R结尾
既然递归函数的含义是求L到R范围上的最长回文子序列长度,那么主函数上就应该这样调用
源码:
👯
public static int f1(char[] str,int L,int R){
if(L==R){
return 1;
}
if(L==R-1){
return str[L]==str[R-1]?2:1;
}
int p1=f1(str,L+1,R-1);
int p2=f1(str, L+1, R);
int p3=f1(str,L,R-1);
int p4=str[L]==str[R]?2+f1(str, L+1, R-1):0;
return Math.max(Math.max(p1,p2),Math.max(p3,p4));
}
public static int longestPalindromeSubseq1(String s) {
if(s==null||s.length()==0){
return 0;
}
char[] str=s.toCharArray();
return f1(str,0,str.length-1);
}
2.3 方法三:动态规划
思路:
根据方法二中的递归函数填表格
源码:
public static int longestPalindromeSubseq2(String s) {
if(s==null||s.length()==0){
return 0;
}
char[] str=s.toCharArray();
int N=str.length;
int[][] dp=new int[N][N];
dp[N-1][N-1]=1;
for (int i = 0; i <N-1 ; i++) {
dp[i][i]=1;
dp[i][i+1]=str[i]==str[i+1] ? 2 : 1;
}
for (int L =N-3; L >=0; L--) {
for (int R = L+2; R < N; R++) {
int p1=dp[L+1][R-1];
int p2=dp[L+1][R];
int p3=dp[L][R-1];
int p4=str[L]==str[R]?2+dp[L+1][R-1]:0;
dp[L][R]= Math.max(Math.max(p1,p2),Math.max(p3,p4));
}
}
return dp[0][N-1];
}
优化:对方法法优化
public static int longestPalindromeSubseq2(String s) {
if(s==null||s.length()==0){
return 0;
}
char[] str=s.toCharArray();
int N=str.length;
int[][] dp=new int[N][N];
dp[N-1][N-1]=1;
for (int i = 0; i <N-1 ; i++) {
dp[i][i]=1;
dp[i][i+1]=str[i]==str[i+1] ? 2 : 1;
}
for (int L =N-3; L >=0; L--) {
for (int R = L+2; R < N; R++) {
int p1=dp[L+1][R-1];
int p2=dp[L+1][R];
int p3=dp[L][R-1];
int p4=str[L]==str[R]?2+dp[L+1][R-1]:0;
dp[L][R]= Math.max(Math.max(p1,p2),Math.max(p3,p4));
}
}
return dp[0][N-1];
}