最長共通部分列LCS
Lintcode 77最長の共通部分列
LCS問題は、最長共通部分列の二つの文字列の合計である
\ [DP [I] [J ] = \左\ {\開始{行列}&MAX(DP [I-1] [J]、DP [I] 、[J-1])、S [I]!= S [j]が\\&DP [I-1] [J-1] + 1、S [I] == S [ J] \端{行列} \
右。\] 多くの問題は、LCSのような問題を解決するために修飾することができます
class Solution {
public:
/**
* @param A: A string
* @param B: A string
* @return: The length of longest common subsequence of A and B
*/
int longestCommonSubsequence(string &A, string &B) {
// write your code here
int n = A.size();
int m = B.size();
std::vector<vector<int>> dp(m+1, vector<int>(n+1, 0)) ;
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
if(A[i-1] == B[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
else dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
return dp[m][n];
}
};
DP [I] [j]はデータのみのi 1とi層を使用して、使用することができるので、アレイをスクロール圧縮空間に、空間の複雑度がそのように(O(MIN(M、\ \)n))を
class Solution {
public:
/**
* @param A: A string
* @param B: A string
* @return: The length of longest common subsequence of A and B
*/
int longestCommonSubsequence(string &A, string &B) {
// write your code here
int n = A.size();
int m = B.size();
std::vector<vector<int>> dp(2, vector<int>(n+1, 0)) ;
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
if(A[i-1] == B[j-1]) dp[i%2][j] = dp[(i-1)%2][j-1] + 1;
else dp[i%2][j] = max(dp[(i-1)%2][j], dp[i%2][j-1]);
}
}
return dp[m%2][n];
}
};
LIS最長増加部分列
ダイナミックプログラミング
DP [i]は= MAX(DP、LIS長の終了時に、DP [I] NUMSである[I]を仮定することができる [J] + 1)(J <I とNUMS [J] <NUMS [I ])、 時間の複雑度\(O(^ N-2)\) 、時間複雑である\(O(N)\)
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n, 1);
int MAX = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < i; j++){
if(nums[i] > nums[j]) dp[i] = max(dp[j] + 1, dp[i]);
}
MAX = max(MAX, dp[i]);
}
return MAX;
}
};
貪欲+半分
まず、V [I]はLIS終了値I-1、最初の元の配列を走査し、比較しNUMS [i]とデータvのプロセス、二分探索最終比率の長さを示す補助配列Vを設定しますNUMS [i]が小さい値と置き換える、そうでない場合には、最後に追加、最終的な長さは、元の配列VのLISの長さであり、時間複雑度\(nlgn \)
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> v;
for(int i = 0; i < n; ++i){
auto loc = lower_bound(v.begin(), v.end(), nums[i]);
if(loc == v.end()) v.push_back(nums[i]);
else *loc = nums[i];
}
return v.size();
}
};
場合にのみ、必要な長さとLISは元の配列を変更することができ、空間的複雑さを低減することができる(\ O(1))\
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
auto p = nums.begin();
auto q = nums.begin();
for(int i = 0; i < n; i++){
auto r = lower_bound(p, q, nums[i]);
if(r == q){ ++q; }
*r = nums[i];
}
return q - p;
}
};
LISとLCSの相互変換
LISのLCSの問題は最初の新しい元の配列ソートされた配列を取得することができる、このような入力配列[5,1,4,2,3]、最長増加するシーケンスである[2,3]のように、問題に変換することができる[1 、2,3,4,5]、その後入力として新しい配列と元の配列は、LCSを解決するために、時間複雑である\(O(N ^ 2) \ )、空間的複雑度\(O(N ^ 2) \ )
LIS LCS問題は、入力配列は、A = [1,7,5,4,8,3,9]、B =として数値の配列であると仮定すると、問題になることができる [1,4,3,5,6,2,8 、9]、及び二つの配列、LCSは、最長共通サブシーケンスの長さを解決する場合、Bは各要素は、(例えば、1-Nが配置されている)に変化する、複雑\(O(N ^ 2 )\)、2つの配列中のA、B各要素が異なるので、我々は、再符号化することができるA = [1,2,3,4,5,6,7](繰り返さない符号化)、BとすることができますBとして符号化= [1,4,6,3,0,0,5,7]要求、LIS長Bの後に再エンコード次いで、及び(0は不在、直接削除されてもよいことを示す)、時間複雑である\ (O(nlgn)\)
LCS最長の回文配列の変異体およびLPS
最長のパリンドローム配列は、最初S Sを反転させたLCS Sのアイデアを要求するために使用することができる「(、次いでLCS S、S)を見つける」
leetcode 516最長の回文シーケンスを
class Solution {
public:
int lcs(string &A, string &B) {
int n = A.size();
int m = B.size();
std::vector<vector<int>> dp(m+1, vector<int>(n+1, 0)) ;
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
if(A[i-1] == B[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
else dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
return dp[m][n];
}
int longestPalindromeSubseq(string s) {
string t(s.rbegin(), s.rend());
return lcs(s, t);
}
};
バリアント:どこでもSは文字回文配列の数の最小値になるように挿入または削除しようとしている中にS
ソリューション:最初の元の長さの長さは、その後、最長の回文配列を見つける-LPS
元のシーケンスの長さを求めるありません
参照
- LISアルゴリズム:最長上昇シーケンス
- 古典的なレースアルゴリズムの概要 - 例27:王子と王女
- アルゴリズムの概要は、第3版は15-2に関する質問します