leetcode 1143. 最長共通部分列
トピックリンク: 1143. 最長共通部分列 - LeetCode
ビデオリンク:動的計画部分列問題の古典的なトピック | LeetCode: 1143. 最長共通部分列_哔哩哔哩_bilibili
トピックの概要
2 つの文字列 text1 と text2 を指定すると、これら 2 つの文字列の最長の共通部分列の長さを返します。
文字列のサブシーケンスとは、そのような新しい文字列を指します。これは、文字の相対的な順序を変更せずに、元の文字列から一部の文字を削除する (または文字をまったく削除しない) ことによって形成される新しい文字列です。
たとえば、「ace」は「abcde」の部分列ですが、「aec」は「abcde」の部分列ではありません。2 つの文字列の「共通部分列」とは、両方の文字列に共通する部分列です。
2 つの文字列に共通のサブシーケンスがない場合は 0 を返します。
例 1:
入力: text1 = "abcde"、text2 = "ace"
出力: 3
説明: 最も長い共通部分シーケンスは「ace」で、長さは 3 です。
例 2:
入力: text1 = "abc"、text2 = "abc"
出力: 3
説明: 最長の共通部分列は「abc」で、その長さは 3 です。
例 3:
入力: text1 = "abc"、text2 = "def"
出力: 0
説明: 2 つの文字列には共通のサブシーケンスがないため、0 を返します。
一連の考え
1. dp 配列の意味を確認する
dp[i][j]: 長さ [0, i - 1] の文字列 text1 と長さ [0, j - 1] の文字列 text2 の最長共通部分列は dp[i][j] です (この定義は、次のコードの実装をより便利にするためのものです)
2. 再帰式を決定する
dp[i][j] = dp[i - 1][j - 1] + 1 (条件: text1[i - 1] が text2[j - 1] と等しい)
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) (条件: text1[i - 1] が text2[j - 1] と等しくない)
3. 配列の初期化
dp[i][0] = 0、dp[0][j] = 0
4. 走査順序を決定する
上の図から、dp[i][j] は左辺、上辺、左上辺から導出できるため、順序は上から下、左から右であることがわかります。
5. 配列を出力します。
コード
lass Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
vector<vector<int>> dp(text1.size() + 1,vector<int>(text2.size() + 1,0));
for(int i = 1;i <= text1.size();i++) {
for(int j = 1;j <= text2.size();j++) {
if(text1[i - 1] == text2[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[text1.size()][text2.size()];
}
};
leetcode 1035. 切り離された行
トピックリンク: 1035. 不連続な行 - LeetCode
ビデオリンク:動的計画法の後続問題、薬を変えずにスープを変える | LeetCode: 1035. Disjoint Lines_哔哩哔哩_bilibili
トピックの概要
整数を2 つの別々の水平線に指定された順序で nums1
書き込み ますnums2
。
nums1[i]和nums2[j]
これで、両方を満たす必要がある 2 つの数値を結ぶ線を引くことができます。
-
nums1[i] == nums2[j]
- また、描かれた線は他の線(非水平線)と交差しません。
線は端点であっても交差できないことに注意してください。各数値は 1 つの線にのみ属することができます。
この方法で線を描画し、描画できる線の最大数を返します。
例 1:
入力: nums1 = [1,4,2]、nums2 = [1,2,4]
出力: 2
説明:上の図に示すように、交差しない 2 本の線を描画できます。
しかし、nums1[1]=4 から nums2[2]=4 までの線が nums1[2]=2 から nums2[1]=2 までの線と交差するため、3 番目の互いに素な線を引くことはできません。
例 2:
入力: nums1 = [2,5,1,2,5]、nums2 = [10,5,2,1,5,2]
出力: 3
一連の考え
実は、この質問の本質は前の質問なのですが、前の質問の記述が変更されており、コードも変わっていませんが、名前が変更されています。この問題は、描画される接続の最大数を見つけることですが、実際には、2 つの文字列の最長の共通部分列の長さを見つけることです。
コード
class Solution {
public:
int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
vector<vector<int>> dp(nums1.size() + 1,vector<int>(nums2.size() + 1,0));
for(int i = 1;i <= nums1.size();i++) {
for(int j = 1;j <= nums2.size();j++) {
if(nums1[i - 1] == nums2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else {
dp[i][j] = max(dp[i][j - 1],dp[i - 1][j]);
}
}
}
return dp[nums1.size()][nums2.size()];
}
};
leetcode 53. 最大サブシーケンス合計
トピックリンク: 53. サブ配列の最大合計 - LeetCode
ビデオリンク:複雑に見えますが、実際は単純な動的プログラミング | LeetCode: 53. 最大部分列 sum_哔哩哔哩_bilibili
トピックの概要
整数配列 nums を指定して、合計が最大になる連続した部分配列を見つけて (部分配列には少なくとも 1 つの要素が含まれます)、その最大合計を返します。
例:
入力: [-2,1,-3,4,-1,2,1,-5,4]
出力: 6
説明: 連続する部分配列 [4,-1,2,1] の合計が最大の 6 です。
一連の考え
この質問は以前にも貪欲に行われました!
次に、モーション ルールを使用してこれをもう一度実行します。
1. dp 配列の意味を確認する
dp[i]: nums[i] (添え字 i を含む) で終わる連続部分列の合計の最大値は dp[i] です
2. 再帰式を決定する
dp[i - 1] + nums[i] (前のサブシーケンスの合計を計算し続ける)
nums[i] (前の連続したサブシーケンスを合計しないで、計算を再開します)
したがって、dp[i] = max(dp[i - 1] + nums[i], nums[i]);
3. 配列の初期化
dp[0] = 数値[0]
4. 走査順序を決定する
前から後ろへ
5. 配列を出力します。
コード
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if(nums.size() == 0) return 0;
vector<int> dp(nums.size());
dp[0] = nums[0];
int result = dp[0];
for(int i = 1;i < nums.size();i++) {
dp[i] = max(dp[i - 1] + nums[i],nums[i]);
if(dp[i] > result) result = dp[i];
}
return result;
}
};