持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情
一、题目
LeetCode 最长回文子序列
给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。
子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
示例 1:
输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 。
示例 2:
输入:s = "cbbd"
输出:2
解释:一个可能的最长回文子序列为 "bb" 。
提示:
1 <= s.length <= 1000
s 仅由小写英文字母组成
二、题解
需要找出字符串s中最长的一个回文子序列的长度,首先回文串就是从左到右读取和从右到左读取都是一样的,而子序列就是字符串中字符串中某些字符的组合,即子序列的字符不一定都要连续出现在字符串中,但是需要注意顺序需要一致。
方法一
根据子序列的定义,首先可以说最长的子序列就是原字符串,那么就可以直接对原字符串来操作获得一个回文串,而子序列的字符不一定是要连序在原字符串的,就是说对于原字符串的字符,如果加上这个字符会导致我当前的回文串不成立,那么我可以选择不加上这个字符,如果加上之后还是符合回文串就更好了。所以可以对原字符串遍历来计算,先定义dp二维数组长度等同于原字符串,而dp[i][j]就表示以s[i]开始到s[j]结束的字符串内最长的回文子序列长度。每一个单独的字符都可以算是一个回文串,所以当i等于j的时候dp[i][j] = 1。然后可以遍历字符串需要保证i < j,当s[i]等于s[j]的时候,如果两边再加上这两个字符那么回文串会变得更长,所以dp[i][j]就应该为前一个最长的回文子序列dp[i + 1][j - 1]加上当前s[i]和s[j]两个字符的长度。如果s[i]不等于s[j]那么就不能再两边加上这两个字符,那么就只能加上其中的一个字符,最后取长度较大的一个即dp[i + 1][j]和dp[i][j - 1]。因为动态规划由较小的字符串来所以需要注意遍历字符串的顺序。
三、代码
方法一 Java代码
class Solution {
public int longestPalindromeSubseq(String s) {
int len = s.length();
int[][] dp = new int[len][len];
for (int i = len - 1; i >= 0; i--) {
dp[i][i] = 1;
for (int j = i + 1; j < len; j++) {
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][len - 1];
}
}
时间复杂度:O(n^2),需要双重循环遍历字符串。
空间复杂度:O(n^2),动态规划需要一个二维数组来计算。