No.516 最长回文子序列
给你一个字符串 s
,找出其中最长的回文子序列,并返回该序列的长度。
子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
示例 1:
输入:s = "bbbab" 输出:4 解释:一个可能的最长回文子序列为 "bbbb" 。
示例 2:
输入:s = "cbbd" 输出:2 解释:一个可能的最长回文子序列为 "bb" 。
提示:
1 <= s.length <= 1000
s
仅由小写英文字母组成
思路分析
其实有一个非常关键的思路。就是
字符串s的最长回文子序列 等价于 字符串s和s的倒序s'的最长公共子序列
简单的证明,s与s倒序的最长公共子序列一定是回文的。正着有,反着有,且相同,这就是回文的定义。
也不会有比s与s倒序的最长公共子序列更长的回文子序列了。如果有,那么s与s倒序的最长公共子序列也会变得更长。
所以我们成功的将题目转化为求s与s倒序的最长公共子序列的长度。
既然是这样,就非常方便了。直接使用二维动态规划即可轻松求解。
首先设置动态规划变量dp[n+1][n+1]
(预留一个空位方便计算)。
再说dp[i][j]
的含义,就是s中前i位与s'中前j位的最长公共子序列。
动态规划的递推式也非常好思考。只要遇到了两个相同的字符,dp[i][j] = dp[i - 1][j - 1] + 1
否则,dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
Rust代码
#![allow(unused)] fn main() { struct Solution; impl Solution { pub fn longest_palindrome_subseq(s: String) -> i32 { let s = s.into_bytes(); let n = s.len(); let mut res = 0; let mut dp = vec![vec![0; n + 1]; n + 1]; for i in 1..=n { for j in 1..=n { if s[n - i] == s[j - 1] { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = dp[i - 1][j].max(dp[i][j - 1]); } } } dp[n][n] } } }