一、题目描述
Given a string s, find the longest palindromic subsequence's length in s.
You may assume that the maximum length of s is 1000.
Input:
"bbbab"
Output:
4
二、题解
方法一:递归(超时)
- 结束条件:
- 两个指针背道而驰时,证明没有找到一个回文串,返回 0
- 当两个指针重合时,代表找到的一个回文串是单个字母本身,返回 1
- 本层递归的责任:
- 定义双指针从字符串 S 的两边向中间逼近,
- 当两个字符相等时
S[l] == S[r]
,我们继续向中间逼近 - 否则,我们只移动一边,这意味着会有两种选择,直接取二者最大值即可。
dfs(l + 1, r)
dfs(l, r - 1)
- 当两个字符相等时
- 定义双指针从字符串 S 的两边向中间逼近,
当 s = "AxxxxA" ,s[l] == s[r]
时
AxxxxA
| |
l r
忽略中间的一串 xxxx,我们已经得到一个长度为 2 的回文子序列,
我们只需让 l++,r-- 去求解规模小 2 的子问题
AxxxxA
| |
l r
当 s = "AxxxxB" ,s[l] != s[r]
时
AxxxxB
| |
l r
这时,我们一次只让字符串的某一边缩减规模,试探哪一种缩减方式可以得到的解最大
缩减方式一:l++
AxxxxB
| |
l r
缩减方式二:r--
AxxxxB
| |
l r
public int longestPalindromeSubseq(String s) {
char[] S = s.toCharArray();
return dfs(S, 0, S.length - 1);
}
private int dfs(char[] S, int l, int r) {
if (l > r) return 0;
if (l == r) return 1;
if (S[l] == S[r]) return dfs(S, l + 1, r - 1) + 2;
else return Math.max(dfs(S, l + 1, r), dfs(S, l, r-1));
}
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,
方法二:记忆化搜索
使用 memo 数组存储指针 l 与 r 在每一个位置时字符串 S 的状态。
int[][] memo = null;
private int dfs(char[] S, int l, int r) {
if (l > r) return 0;
if (l == r) return 1; //只有一个字母时,当然返回1
if (memo[l][r] != 0)
return memo[l][r];
if (S[l] == S[r]) memo[l][r] = dfs(S, l + 1, r- 1) + 2;
else memo[l][r] = Math.max(dfs(S, l + 1, r), dfs(S, l, r-1));
return memo[l][r];
}
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,
方法三;二维 dp
- 定义状态:
- 表示字符串 内部回文子序列最长长度。
- 思考状态转移方程:
- 当 时, ,表示在先前找到的 LPS 的长度基础上 +2.
- 当
时,两边各自移动一步,取两种状态的最大值。
- 思考初始化:
- 表示单个字母的回文子序列长度为 1.
- 思考输出: ,由上面的状态可将定义为 字符串 内部 LPS 的最大长度。
public int longestPalindromeSubseq2(String s) {
int N = s.length();
char[] S = s.toCharArray();
int[][] dp = new int[N][N];
//最短回文子序列为自身
for (int i = 0; i < N; i++)
dp[i][i] = 1;
for (int i = N-1; i >= 0; i--)
for (int j = i+1; j < N; j++) {
if (S[i] == S[j]) dp[i][j] = dp[i+1][j-1] + 2;
else dp[i][j] = Math.max(dp[i][j-1], dp[i+1][j]);
}
return dp[0][N-1];
}
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,
方法四:一维 dp