题意整理

  • 给定字符串。
  • 求这个字符串的最长回文子序列。
  • 回文序列是指,如果将原序列翻转后与原序列相等,那么这个序列是回文序列。

方法一(记忆化递归)

1.解题思路

  • 递归终止条件:左边界和右边界之间只含一个元素,或只含两个元素。
  • 递归如何推进:如果左边界和右边界相等,则可以将两边分别向中间压缩一步,并且长度减二;否则,要么向左压缩,要么向右压缩,取两者较大值。
  • 每一层递归返回什么:当前层的最长回文子序列长度。

由于普通的递归会有很多重复的计算,可以用记忆数组记录已知的情况。

2.代码实现

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param s string 一个字符串由小写字母构成,长度小于5000
     * @return int
     */
    //声明记忆数组
    int[][] memo;

    public int longestPalindromeSubSeq (String s) {
        //转化为字符数组
        char[] arr=s.toCharArray();
        //new一个记忆数组
        memo=new int[arr.length][arr.length];
        //返回左边界为0,右边界为n-1的情况
        return dfs(arr,0,arr.length-1);
    }

    private int dfs(char[] arr,int l,int r){
        //如果记忆数组被赋值过,直接返回
        if(memo[l][r]!=0) return memo[l][r];
        //左边界和有边界在同一位置,即只有一个字符的情况
        if(l==r){
            memo[l][r]=1;
            return 1;
        } 
        //左边界和有边界值相等,且只有左右边界两个索引
        if(l+1==r&&arr[l]==arr[r]){
            memo[l][r]=2;
            return 2;
        }
        //左边界和有边界值相等,可以向中间压缩
        if(arr[l]==arr[r]){
            memo[l][r]=dfs(arr,l+1,r-1)+2;
            return dfs(arr,l+1,r-1)+2;
        }
        //左边界和有边界值不相等,要么压缩左边界,要么压缩右边界
        memo[l][r]=Math.max(dfs(arr,l+1,r),dfs(arr,l,r-1));
        return Math.max(dfs(arr,l+1,r),dfs(arr,l,r-1));
    }


}

3.复杂度分析

  • 时间复杂度:最多递归图片说明 次,所以时间复杂度是图片说明
  • 空间复杂度:需要额外深度为n的递归栈以及大小为图片说明的记忆数组,所以空间复杂度为图片说明

方法二(动态规划)

1.解题思路

  • 状态定义:图片说明 表示原字符串起点为i,终点为j的子序列的最长回文子序列的长度。
  • 初始化:只要起点和终点在一起,那么最长回文子序列长度就是1。
  • 状态转移:如果i索引和j索引处字符相等,那么可以由i+1和j-1分别向左和向右扩散,即图片说明 ;如果i索引和j索引处字符不相等,要么取i+1为起点j为终点的串,要么取i为起点j-1为终点的串,即图片说明

动图展示:
图片说明

2.代码实现

import java.util.*;

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param s string 一个字符串由小写字母构成,长度小于5000
     * @return int
     */
    public int longestPalindromeSubSeq (String s) {
        //初始化dp数组
        int n=s.length();
        int[][] dp=new int[n][n];

        for(int i=n-1;i>=0;i--){
            //如果只有一个字符,长度必为1
            dp[i][i]=1;
            for(int j=i+1;j<n;j++){
                //如果i索引和j索引处字符相等,那么可以由i+1和j-1分别向左和向右扩散
                if(s.charAt(i)==s.charAt(j)){
                    dp[i][j]=dp[i+1][j-1]+2;
                }
                //如果不相等,要么取i+1为起点j为终点的串,要么取i为起点j-1为终点的串
                else{
                    dp[i][j]=Math.max(dp[i+1][j],dp[i][j-1]);
                }
            }
        }
        //返回起点为0,终点为n-1之间的最长回文子序列长度
        return dp[0][n-1];
    }
}

3.复杂度分析

  • 时间复杂度:需要遍历图片说明 次,所以时间复杂度是图片说明
  • 空间复杂度:需要额外大小为图片说明的dp数组,所以空间复杂度为图片说明