1143. Longest Common Subsequence
阿新 • • 發佈:2020-09-06
問題:
給定兩個字串,求他們的最長公共子序列的長度。
Example 1: Input: text1 = "abcde", text2 = "ace" Output: 3 Explanation: The longest common subsequence is "ace" and its length is 3. Example 2: Input: text1 = "abc", text2 = "abc" Output: 3 Explanation: The longest common subsequence is "abc" and its length is 3. Example 3: Input: text1 = "abc", text2 = "def" Output: 0 Explanation: There is no such common subsequence, so the result is 0. Constraints: 1 <= text1.length <= 1000 1 <= text2.length <= 1000 The input strings consist of lowercase English characters only.
解法:DP(動態規劃)
1.確定【狀態】:
- 字串text1的第i個字元:text1[i]
- 字串text2的第j個字元:text2[j]
2.確定【選擇】:分兩種情況
- text1[i] == text2[j]:
- 前一個字元狀態(公共序列長度)+1: dp[i-1][j-1] + 1
- text1[i] != text2[j]:有以下3種情況,取最大值。
- 只有text1[i]是最終公共子序列的一個字元 -> =上一個包含text1[i]而不包含text2[j]的字元狀態:dp[i][j-1]
- 只有text2[j]是最終公共子序列的一個字元->=上一個不包含text1[i]而包含text2[j]的字元狀態:dp[i-1][j]
- 兩個字元都不是最終公共子序列的一個字元 ->=上一個既不包含text1[i]又不包含text2[j]的字元狀態:dp[i-1][j-1]
- ★由於dp[i-1][j-1]一定<=dp[i][j-1] or dp[i-1][j],因此可以省略比較dp[i-1][j-1]
3. dp[i][j]的含義:
對比到text1的第 i 個字元,text2的第 j 個字元為止,兩個字串的最大公共子序列長度。
4. 狀態轉移:
dp[i][j]=
- (text1[i] == text2[j]):=前一個字元狀態+1:dp[i-1][j-1] + 1
- (text1[i] != text2[j]):=max {
- 上一個包含text1[i]字元的狀態:dp[i][j-1]
- 上一個包含text2[j]字元的狀態:dp[i-1][j]
- 上一個text1[i]text2[j]都不包含的狀態:dp[i-1][j-1](★可省略) }
5. base case:
- dp[0][j]=0:text1為空串,則其與text2的公共子序列也一定為空串,長度為0。
- dp[i][0]=0:text2為空串,則其與text1的公共子序列也一定為空串,長度為0。
程式碼參考:
1 class Solution { 2 public: 3 //dp[i][j]:until text1[i],text2[j]. the max length of the LCS 4 //case_1: text1[i]==text2[j]: 5 // this character must be in LCS. we need add 1 to pre status:dp[i-1][j-1]. 6 // =dp[i-1][j-1]+1 7 //case_2: text1[i]!=text2[j] 8 // text1[i] or text2[j] may in LCS. we choose the max of these possibilities. 9 // if text1[i] is in LCS, 10 // = dp[i][j-1] 11 // if text2[j] is in LCS, 12 // = dp[i-1][j] 13 // if none of them is in LCS, 14 // = dp[i-1][j-1] 15 // note: dp[i-1][j-1]<=dp[i-1][j](dp[i][j-1]) ,so we can ignore it 16 // =max(dp[i][j-1],dp[i-1][j]) 17 //base case: 18 //dp[i][0]=0 19 //dp[0][j]=0 20 int longestCommonSubsequence(string text1, string text2) { 21 int m = text1.length(), n = text2.length(); 22 vector<vector<int>> dp(m+1, vector<int>(n+1, 0)); 23 for(int i=1; i<=m; i++) { 24 for(int j=1; j<=n; j++) { 25 if(text1[i-1]==text2[j-1]) { 26 dp[i][j]=dp[i-1][j-1]+1; 27 } else { 28 dp[i][j]=max(dp[i-1][j], dp[i][j-1]); 29 } 30 } 31 } 32 return dp[m][n]; 33 } 34 };
♻️ 優化:
空間複雜度:2維->1維
去掉 i
壓縮所有行到一行。
左上角dp[i-1][j-1]會被下面的dp[i][j-1]覆蓋,因此引入變數pre,在更新dp[i][j-1]之前,儲存dp[i-1][j-1]
程式碼參考:
1 class Solution { 2 public: 3 int longestCommonSubsequence(string text1, string text2) { 4 int m = text1.length(), n = text2.length(); 5 vector<int> dp(n+1, 0); 6 for(int i=1; i<=m; i++) { 7 int pre = 0; 8 for(int j=1; j<=n; j++) { 9 int tmp = dp[j]; 10 if(text1[i-1]==text2[j-1]) { 11 dp[j]=pre+1; 12 } else { 13 dp[j]=max(dp[j], dp[j-1]); 14 } 15 pre = tmp; 16 } 17 } 18 return dp[n]; 19 } 20 };