回文序列是DP应用中经常见到的一类问题,这里的回文指的是一个字串,其字面从头到尾的排列顺序和从尾到头的排列顺序相同。 最长回文子序列问题的题面为:
给定一个长度为N的字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。 子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
实例如下:
s = "bbbab"
答案为4,最长回文子序列为 "bbbb" 。
解题思路:
我们可以定义一个表达式F(i, j),其中i是字符串的开始下标,j是结束下标,F(i, j)返回的是从i到j的子字符串中的最长回文序列的长度。
根据题面的要求,我们有以下两种情况
- s[i]与s[j]相同,在该情况下,如果i和j相同,那么F(i, j)返回的就是1,否则F(i, j)返回 2+ F(i + 1, j - 1)
- s[i]与s[j]不同,在该情况下,F(i, j)返回的是max(F(i + 1, j), F(i, j - 1))
边界情况是当i>j时,F(i, j)始终返回0。
从以上表达式出发,我们可以定义一个(N+1) * (N+1)的二维数组作为DP存储,然后使用双重循环,外层循环依据字串下标从大到小,内层循环依据字串下标从小到大,来依次计算DP数组中各个元素的值。最后DP[1][N]中的数值就是问题的最终答案。
Java代码如下:
class Solution {
public int longestPalindromeSubseq(String s) {
char[] chars = s.toCharArray();
int N = chars.length;
int[][] dp = new int[N + 1][N + 1];
for (int i = N; i >= 1; i --) {
for (int j = i; j <= N; j ++) {
int len = 0;
if (i == j) {
len = 1;
} else {
if (chars[i - 1] == chars[j - 1]) {
len = 2 + dp[i + 1][j - 1];
} else {
len = Math.max(dp[i][j - 1], dp[i + 1][j]);
}
}
dp[i][j] = len;
}
}
return dp[1][N];
}
}