package 左神题目.dp;
//给定--个字符串str,返回这个字符串的最长回文子序列长度比如str = "a12b3c43def2ghi1kpm”
// 最长回文子序列是“1234321" 或者“123c321"返回长度7
import java.util.Scanner;
//做动态规划,最好做出严格表依赖结构,建立空间感,方便优化
//动态规划一定是递归改出来的,但是递归不一定能改动态规划
public class longestPalindromeSubseq {
//1.反转字符串,然后求两个字符串的最长公共子序列
//怎么求最长公共子序列,请看上一篇文章
public static int win1(String s){
//字符串反转
String s2 =new StringBuilder(s).reverse().toString();
char[] str1 = s.toCharArray();
char[] str2 = s2.toCharArray();
int N=str1.length;
int[][] dp = new int[N][N];
dp[0][0] = str1[0]==str2[0]?1:0;
for(int j=1;j<N;j++){
dp[0][j] = str1[0]==str2[j]?1:dp[0][j-1];
}
for(int i=1;i<N;i++){
dp[i][0] = str1[i]==str2[0]?1:dp[i-1][0];
}
for(int i=1;i<N;i++){
for(int j=1;j<N;j++){
int p1 = dp[i-1][j];
int p2 = dp[i][j-1];
int p3 = str1[i]==str2[j]?(1+dp[i-1][j-1]):0;
dp[i][j] = Math.max(p1,Math.max(p2,p3));
}
}
return dp[N-1][N-1];
}
//2.暴力法
public static int win2(String s){
if(s==null || s.length()==0){
return 0;
}
char[] str = s.toCharArray();
return process(str,0,str.length-1);
}
public static int process(char[] str,int l,int r){
if(l==r){
return 1;
}
if(l==r-1){
return str[l]==str[r]?2:1;
}
//不考虑以l开头和以j结尾
int p1 = process(str,l+1,r-1);
//只考虑以l开头,不考虑以j结尾
int p2 = process(str,l,r-1);
//不考虑以l开头,只考虑以j结尾
int p3 = process(str,l+1,r);
//同时考虑以l开头和以j结尾
int p4 = str[l]==str[r]?(2+process(str,l+1,r-1)):0;
//p1和p4可以写一起 p4 = str[l]==str[r]?(2+process(str,l+1,r-1)):process(str,l+1,r-1);
return Math.max(p1,Math.max(p2,Math.max(p3,p4)));
}
//3.动态规划
public static int win3(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;
}
//2-N列,从第三条对角线开始填
// for(int i = 2;i<N;i++){
// int r=i;
// //0~N-i行
// for(int l = 0;l<N-i;l++,r++){
// int p1 = dp[l+1][r-1]; // --> p1 = process(str,l+1,r-1);
// int p2 = dp[l][r-1]; // --> p2 = process(str,l,r-1);
// int p3 = dp[l+1][r]; // --> p3 = process(str,l+1,r);
// int p4 = str[l]==str[r]?2+p1:0; // -->p4 = str[l]==str[r]?(2+process(str,l+1,r-1)):0;
//
// dp[l][r]=Math.max(p1,Math.max(p2,Math.max(p3,p4)));
// }
// }
//行 从下往上填
// 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][r-1];
// int p3 = dp[l+1][r];
// int p4 = str[l]==str[r]?2+p1:0;
// //p1 p4 ---> p =str[l]==str[r]? 2+dp[l+1][r-1]:dp[l+1][r-1];
// dp[l][r]=Math.max(p1,Math.max(p2,Math.max(p3,p4)));
// }
// }
for(int l = N-3;l>=0;l--){
for(int r = l+2;r<N;r++){
//为什么不要p1了,因为dp[l+1][r]是他的三个方向的最大值,同理dp[l][r-1]也是
//因为dp[l+1][r-1]是p2的下,p3的左,p2,p3都是大于等于p1的,所以没必要写p1的
//只用判断一下p1+2
dp[l][r] = Math.max(dp[l+1][r],dp[l][r-1]);
if(str[l]==str[r]){
dp[l][r] = Math.max(dp[l][r],2+dp[l+1][r-1]);
}
}
}
return dp[0][N-1];
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
String s = sc.next();
long start1 = System.currentTimeMillis();
System.out.println(win1(s));
long end1 = System.currentTimeMillis();
System.out.println("cost time:" + (end1-start1)+"ms");
long start2 = System.currentTimeMillis();
System.out.println(win2(s));
long end2 = System.currentTimeMillis();
System.out.println("cost time:" + (end2-start2)+"ms");
long start3 = System.currentTimeMillis();
System.out.println(win3(s));
long end3 = System.currentTimeMillis();
System.out.println("cost time:" + (end3-start3)+"ms");
}
}
最长回文子序列(教你由暴力递归改动态规划)
最新推荐文章于 2022-10-19 21:53:44 发布