这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战
516. 最长回文子序列
给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。
子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
示例 1:
输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 。
示例 2:
输入:s = "cbbd"
输出:2
解释:一个可能的最长回文子序列为 "bb" 。
提示:
1 <= s.length <= 1000s仅由小写英文字母组成
方法一
看到题目,求字符串的回文串,首先想到的方法有,字符串哈希(子序列可以不连续,排除),马拉车(不会),动态规划,暴力;
排除了字符串哈希和马拉车,没得选择了,当然是选择动态规划了,因为暴力分析一下就能得知会超时;
那么,动态规划该如何做呢?一般来说,动态规划都会选择最后一个元素来考虑,不妨就先这么来分析一下;
假设状态表示为f[i][j],为什么是二维,经验所得,意思为从下标为i到下标为j的回文子序列的方案,值为方案中的回文串的长度最大值;
- 当
s[i]==s[j]时,i到j的回文方案为i+1到j-1的方案加上两个端点i,j,即f[i][j]=f[i+1][j-1] +2 - 当
s[i]!=s[j]时,两个端点不相等,那么我们就不让这两个端点配对,i尝试和j-1配对,即f[i][j-1],j尝试和i+1配对,即f[i+1][j],综合一下,f[i][j]=max(f[i+1][j],f[i][j-1])
class Solution {
public int longestPalindromeSubseq(String s) {
int n = s.length(), res = 0;
int[][] f = new int[n][n];
for (int len = 1; len <= n; len ++ ) {
for (int l = 0; l + len - 1 < n; l ++ ) {
int r = l + len - 1;
if (len == 1) f[l][r] = 1;
else {
if (s.charAt(l) == s.charAt(r)) {
if (l + 1 > r - 1) f[l][r] = 2;
else f[l][r] = f[l + 1][r - 1] + 2;
}else f[l][r] = Math.max(f[l + 1][r], f[l][r - 1]);
}
}
}
return f[0][n - 1];
}
}
时间复杂度: O(n*n)
空间复杂度: O(n*n)