最长重复子数组最长公共子序列不相交的线
引言
這同樣是兩種類型的題目,有很多相似的地方和不同的地方,區別依然是連續和不連續之分。
最長重復子數組
給兩個整數數組 A 和 B ,返回兩個數組中公共的、長度最長的子數組的長度。
示例:
輸入:
A: [1,2,3,2,1]
B: [3,2,1,4,7]
輸出:3
解釋:
長度最長的公共子數組是 [3, 2, 1] 。
提示:
1 <= len(A), len(B) <= 1000
0 <= A[i], B[i] < 100
這道題中說的子數組,其實就是連續的子序列,
1,dp[i][j] 表示以下標i - 1為結尾的A,和以下標j - 1為結尾的B,最長重復子數組長度為dp[i][j]。
2,因為dp[i][j]是由dp[i - 1][j - 1]推導出來的。所以當A[i - 1] 和B[j - 1](對應的是dp[i][j],dp[i - 1][j - 1]對應的是A[i - 2] 和B[j - 2])相等的時候,dp[i][j] = dp[i - 1][j - 1] + 1;
3,dp[i][0] 和dp[0][j]初始化為0,因為當一個字符數組不存在時肯定沒有公共長度,且在這里其實也是沒有意義的,因為A,B長度規定大于等于1,所以不會為0,但是為了轉移方程的計算,所以還是需要進行初始化;
4,循環肯定是從前往后的,A,B二重循環誰在里面誰在都一樣;
代碼如下:
class Solution { public:int findLength(vector<int>& nums1, vector<int>& nums2) {int len1 = nums1.size(), len2 = nums2.size();vector<vector<int>> dp(len1 + 1, vector<int> (len2 + 1, 0));int ans = 0;for (int i = 1; i <= len1; ++i) {for (int j = 1; j <= len2; ++j) {if (nums1[i - 1] == nums2[j - 1])dp[i][j] = dp[i - 1][j - 1] + 1;ans = max(ans, dp[i][j]);}}return ans;} };這道題是所求的子序列是連續的,下面來一道相似的題目不連續的;
最長公共子序列
給定兩個字符串 text1 和 text2,返回這兩個字符串的最長公共子序列的長度。
一個字符串的 子序列 是指這樣一個新的字符串:它是由原字符串在不改變字符的相對順序的情況下刪除某些字符(也可以不刪除任何字符)后組成的新字符串。
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。兩個字符串的「公共子序列」是這兩個字符串所共同擁有的子序列。
若這兩個字符串沒有公共子序列,則返回 0。
示例 1:
輸入:text1 = “abcde”, text2 = “ace”
輸出:3
解釋:最長公共子序列是 “ace”,它的長度為 3。
示例 2:
輸入:text1 = “abc”, text2 = “abc”
輸出:3
解釋:最長公共子序列是 “abc”,它的長度為 3。
示例 3:
輸入:text1 = “abc”, text2 = “def”
輸出:0
解釋:兩個字符串沒有公共子序列,返回 0。
提示:
1 <= text1.length <= 1000
1 <= text2.length <= 1000
輸入的字符串只含有小寫英文字符。
這道題就是求的不連續的公共子序列,和上一道題非常像,分析內容也基本相同,這里就著重分析不同的地方:轉移方程;
1, dp[i][j]是長度為[0, i - 1]的字符串text1與長度為[0, j - 1]的字符串text2的最長公共子序列;(加深印象)
2,這里就分為了兩種情況:
text1[i - 1] 與 text2[j - 1]相同,text1[i - 1] 與 text2[j - 1]不相同
如果text1[i - 1] 與 text2[j - 1]相同,那么找到了一個公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;
如果text1[i - 1] 與 text2[j - 1]不相同,那就看看text1[0, i - 2]與text2[0, j - 1]的最長公共子序列(dp[i - 1][j]) 和 text1[0, i - 1]與text2[0, j - 2]的最長公共子序列(dp[i][j - 1]),取最大的。
則有dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
其它的就一樣了,代碼如下:
class Solution { public:int longestCommonSubsequence(string text1, string text2) {int len1 = text1.size(), len2 = text2.size();vector<vector<int>> dp(len1 + 1, vector<int> (len2 + 1, 0));for (int i = 1; i <= len1; ++i) {for (int j = 1; j <= len2; ++j) {if (text1[i - 1] == text2[j - 1])dp[i][j] = dp[i - 1][j - 1] + 1;elsedp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);}}return dp[len1][len2];} };下面的這一道也是求不連續的子序列,但是不容易看出來,其實本質上和最長公共子序列這一道題一模一樣;
不相交的線
在兩條獨立的水平線上按給定的順序寫下 nums1 和 nums2 中的整數。
現在,可以繪制一些連接兩個數字 nums1[i] 和 nums2[j] 的直線,這些直線需要同時滿足滿足:
nums1[i] == nums2[j]
且繪制的直線不與任何其他連線(非水平線)相交。
請注意,連線即使在端點也不能相交:每個數字只能屬于一條連線。
以這種方法繪制線條,并返回可以繪制的最大連線數。
示例 1:
輸入:nums1 = [1,4,2], nums2 = [1,2,4]
輸出:2
解釋:可以畫出兩條不交叉的線,如上圖所示。
但無法畫出第三條不相交的直線,因為從 nums1[1]=4 到 nums2[2]=4 的直線將與從 nums1[2]=2 到 nums2[1]=2 的直線相交。
示例 2:
輸入:nums1 = [2,5,1,2,5], nums2 = [10,5,2,1,5,2]
輸出:3
示例 3:
輸入:nums1 = [1,3,7,1,7,5], nums2 = [1,9,2,5,1]
輸出:2
提示:
1 <= nums1.length <= 500
1 <= nums2.length <= 500
1 <= nums1[i], nums2[i] <= 2000
直線不能相交,那么只要在字符串A中找到一個與字符串B相同的子序列,且這個子序列不能改變相對順序,那么連接相同數字的直線就不會相交。
其實就是從字符串B中找字符串A的最長公共子序列;
代碼一模一樣:
class Solution { public:int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {int len1 = nums1.size(), len2 = nums2.size();vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1, 0));for (int i = 1; i <= len1; ++i) {for (int j = 1; j <= len2; ++j) {if (nums1[i - 1] == nums2[j - 1])dp[i][j] = dp[i - 1][j - 1] + 1;elsedp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);}}return dp[len1][len2];} };總結
這類的題目多數都需要用到所給的兩個字符串或數組長度來創建一個二維dp數組,可以發現這三道題的dp數組定義幾乎是一模一樣的,所以再遇到類似的題目需要向這個方向靠攏;
還需要注意題目要求是否連續,對應著不同的處理方法;
總結
以上是生活随笔為你收集整理的最长重复子数组最长公共子序列不相交的线的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 代码格式驼峰命名法
- 下一篇: 判断子序列不同的子序列两个字符串的删除操