文字列
s
を指定して、最長の回文サブシーケンスを見つけ、シーケンスの長さを返します。s
最大長さを想定することができます1000
。注:サブシーケンスは連続している必要はありません
例1:
输入"bbbab"
输出 4
一个可能的最长回文子序列为 "bbbb"。
例2:
输入"cbbd"
输出 2
一个可能的最长回文子序列为 "bb"。
ここでdp
配列を定義するのは次のとおりです。部分文字列s[i..j]
、最長のシーケンスの長さは回文dp[i][j]
です。
具体的にはdp[i][j]
、子供が問題のdp[i+1][j-1]
結果(s[i+1..j-1]
サブレングスの最長のパリンドロームシーケンス)を知っていると仮定して、dp[i][j]
(s[i..j]
サブレングスのパリンドロームシーケンスの長さで)値を計算する方法を見つけることができるかどうかを尋ねたい場合はどうなりますか?
できる!それは異なりs[i]
、s[j]
文字:
Taliaが等しい場合は、Taliaを追加します。s[i+1..j-1]
最長のパリンドロームシーケンスは、最長のパリンドロームシーケンスの子ですs[i..j]
。
タリアと等しくない場合、タリアは説明の可能性が低くs[i..j]
、最長のパリンドロームシーケンスがであるため、タリアが追加さs[i+1..j-1]
れ、サブストリングの長いパリンドロームシーケンスが生成されるかどうかを確認します。
上記の2つのケースは、次のようなコードで記述されています。
if(s.charAt(i) == s.charAt(j)){
dp[i][j] = dp[i+1][j-1] + 2;
}else{
dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
}
これまでのところ、定義上、dp配列を書き出すための状態遷移方程式はdp[0][n - 1]
、つまりs
、最長のパリンドロームシーケンスの全長です。
最初に基本ケースを明確にします。文字が1つしかない場合、明らかに最長の回文サブシーケンスの長さは1、つまりですdp[i][j] = 1 (i == j)
。
のでi
、確かに少ないj
ので、これらのためのi > j
位置、シーケンスはゼロに初期化されなければならないものを単純にありません。
また、書き込まれたばかりの状態遷移方程式を見て、私はお願いしたいと思いdp[i][j]
、あなたが知っている必要がありdp[i+1][j-1]
、dp[i+1][j]
、dp[i][j-1]
三つの位置を、
dp配列に入力した後、次のようになっていると判断したベースケースを見てみましょう。
dp[i][j]
計算ごとに左下と右下の方向の位置が計算されていることを確認するために、斜めまたは逆方向にのみ移動できます。
- 斜めにトラバースする
class Solution {
public int longestPalindromeSubseq(String s) {
int n = s.length();
int[][] dp = new int[n][n];
for(int i = 0; i <= n-1; i++){
dp[i][i] = 1;
}
for(int j = 1; j <= n-1; j++){
for(int i = 0; i <= n-j-1; i++){
if(s.charAt(i) == s.charAt(j+i)){
dp[i][j+i] = dp[i+1][j+i-1] + 2;
}else{
dp[i][j+i] = Math.max(dp[i+1][j+i], dp[i][j+i-1]);
}
}
}
return dp[0][n-1];
}
}
- 逆トラバーサル
class Solution {
public int longestPalindromeSubseq(String s) {
int n = s.length();
int[][] dp = new int[n][n];
for(int i = n-1; i >= 0; i--){
dp[i][i] = 1;
for(int j = i+1; j < n; j++ ){
if(s.charAt(i) == s.charAt(j)){
dp[i][j] = dp[i+1][j-1] + 2;
}else{
dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
}
}
}
return dp[0][n-1];
}
}
時間計算量:O(n ^ 2)
スペースの複雑さ:O(n ^ 2)