给定一个字符串s,找到其中最长的回文子序列。可以假设s的最大长度为1000
示例 1:
输入:”bbbab”
输出:4
一个可能的最长回文子序列为 “bbbb”,长度为4
示例 2:
输入:”cbbd”
输出:2
一个可能的最长回文子序列为 “bb”,长度为2
注意是子序列而不是子串!
子串是连续的,比如”abda”最长回文子串就是”a”或者”b”..
子序列是不连续的,比如”abda”最长子序列就是”aba”或者”ada”
解题思路:
aba 因为头和尾相等,所以最长子序列就是2+中间b的最长子序列=2+1=3
abb 因为头和尾不相等,所以最长子序列就是ab或者bb的最长子序列=max(ab,bb)=max(1,2)=2
所以我们得到下面的结论
假设非空字符串x1x2…xn,如果x1==xn,那么max=2+max(x2…xn-1);如果不相等的话就是max(x1..xn-1,x2…xn)
(这个思路我是没想到的,借鉴了其他博主的思路,确实这个思路是很难的,比写代码还难,这个思路没有的话,很难去解题)
看懂了这个思路,我心急火燎的想到了递归
class Solution {
public int longestPalindromeSubseq(String s) {
if(s==null||s.length()==0) return 0;
if(s.length()==1) return 1;
char[] chars = s.toCharArray();
if(s.length()==2) return chars[0] == chars[1] ? 2 : 1;
if(chars[0]==chars[s.length()-1]){
return 2 + longestPalindromeSubseq(s.substring(1,s.length()-1));
}else {
return Math.max(longestPalindromeSubseq(s.substring(0, s.length() - 1)), longestPalindromeSubseq(s.substring(1, s.length())));
}
}
}
但是运行的结果是跑到第61个测试用例的时候超出时间限制,说明这种递归太耗时了,毕竟题目故意让字符串变得很大,也说明很多人都能想到递归,但是出题者就是不想让你这么做!
所以在上述解题思路上我们需要增加动态规划的思想,一个字符一个字符的增加,求其最大序列,比如a最长子序列是1,ab呢?求出ab再加一个c,abc呢?再求出abc,然后依次加完所有的字符
class Solution:
def longestPalindromeSubseq(self, s):
"""
:type s: str
:rtype: int
"""
length = len(s)
if length == 0 or length == 1:
return length
if s == s[::-1]:
return length
p = [[0] * length for i in range(length)]
for j in range(0, length):
p[j][j] = 1
for i in reversed(range(0, j)):
if s[i] == s[j]:
p[i][j] = p[i + 1][j - 1] + 2
else:
p[i][j] = max(p[i+1][j], p[i][j-1])
return p[0][length-1]
虽然是Python写的但是没关系,我们看下他的思路然后吸取思路然后转成对应的语言即可
我们写个例子就可以知道其思路:
给定一个字符串”aba”寻找最大的子序列
水平方向表示a到a所能组成的最大子序列,比如3表示的就是第一个a到第三个a的最大子序列是3,3左边的1表示的就是从a到b的最大子序列是1
竖直方向我们是从下到上,3表示的是从第三个a到第一个a所能组成的最大子序列长度,3下面的1表示的是从第三个a到b所能组成的最大子序列是1
所以我们最后找到这个二维数组最右上的那个元素就是最长的子序列个数了
我们从a开始,依次加上b,a两个字符
a就一个字符,就是1
ab的话,看竖直方向,b本身是一个字符,所以放入1进入数组,b和第一个a比较的时候发现不一样,于是找a或者b所能构成的最大子序列,于是max(1,1)=1 也就是ab所能构成的最大子序列就是1
aba的话,竖直方向上,第三个a初始值也是1,第三个a不和b一直,所以取最大仍然是1,第三个a和第一个a比较发现相等,这个时候就需要注意了,首尾字符相等我们的思路是去掉两个字符剩下的字符所能组成的最大序列,因此是3=左下角的1+2,左下角表示的就是除去两个字符所能组成的最大子序列!
JAVA代码
public int longestPalindromeSubseq(String s) {
if (s == null || s.length() == 0) return 0;
if(s.length()==1) return 1;
char[] chars = s.toCharArray();
if(s.length()==2) return chars[0] == chars[1] ? 2 : 1;
//三个字符才开始循环判断
int[][] array = new int[s.length()][s.length()];
for (int i = 0; i < s.length(); i++) {
array[i][i] = 1;
for (int j = i-1; j >-1 ; j--) {
if(chars[j]==chars[i]){
array[i][j] = 2 + array[i - 1][j + 1];
}else {
array[i][j] = Math.max(array[i-1][j],array[i][j+1]);
}
}
}
return array[chars.length-1][0];
}