每天一道LeetCode-----计算两个序列最长的公共子序列长度
原題鏈接Maximum Length of Repeated Subarray
計(jì)算兩個(gè)序列最長(zhǎng)的相同子序列的長(zhǎng)度
簡(jiǎn)單暴力的方法是對(duì)A中每個(gè)元素遍歷一遍序列B,找到相同的位置后計(jì)算從這個(gè)位置開(kāi)始有多少個(gè)元素和A是相同的,唔…大概是這個(gè)樣子
class Solution { public:int findLength(vector<int>& A, vector<int>& B) {int maxLen = 0;for(int i = 0; i < A.size(); ++i){for(int j = 0; j < B.size(); ++j){if(A[i] != B[j])continue;int len = 0;int p = i, q = j;while(p != A.size() && q != B.size() && A[p] == B[q]){++p;++q;++len;}maxLen = std::max(maxLen, len);}}return maxLen;} };不過(guò)超時(shí)了:(,想想也覺(jué)得沒(méi)這么容易
假設(shè)序列A的長(zhǎng)度為m,序列B的長(zhǎng)度為n。
考慮兩個(gè)序列的相同子序列
A[i, i+1, ..., i+k] B[j, j+1, ..., j+k]將這兩個(gè)子序列分別延伸到序列末尾
A[i, i+1, ..., m-1] B[j, j+1, ..., n-1]那么,可以把公共子序列看成是序列A[i : m-1]和B[j : n-1]的最長(zhǎng)公共前綴
轉(zhuǎn)換成動(dòng)態(tài)規(guī)劃,設(shè)dp[i][j]表示A[i : m-1]和B[j : n-1]的最長(zhǎng)公共前綴,那么
if(A[i] == B[j])dp[i][j] = 1 + dp[i + 1][j + 1]; elsedp[i][j] = 0;如果A[i] == B[j],那么A[i : m-1]和B[j : n-1]的第一個(gè)位置已經(jīng)相等,只需計(jì)算A[i+1 : m-1]和B[j+1 : n-1]的最長(zhǎng)公共前綴
如果A[i] != B[j],那么A[i : m-1]和B[j : n-1]第一個(gè)位置就不相等,顯然沒(méi)有公共前綴
代碼如下
class Solution { public:int findLength(vector<int>& A, vector<int>& B) {vector<vector<int>> dp(A.size()+1, vector<int>(B.size()+1, 0));int maxLen = 0;for(int i = A.size() - 1; i >= 0; --i){for(int j = B.size() - 1; j >= 0; --j){if(A[i] == B[j]){/* 更新dp[i][j] */dp[i][j] = 1 + dp[i + 1][j + 1];maxLen = std::max(maxLen, dp[i][j]);}}}return maxLen;} };當(dāng)然了,既然是迭代法實(shí)現(xiàn)的動(dòng)態(tài)規(guī)劃,那么就能將動(dòng)態(tài)規(guī)劃數(shù)組降維
觀察二維動(dòng)態(tài)規(guī)劃數(shù)組的更新情況,整個(gè)程序只用到了dp[i][j]和dp[i+1][j+1],又因?yàn)閕在外層循環(huán),所以可以改為dp[j] = dp[j+1],不過(guò)需要改變內(nèi)層循環(huán)的遍歷順序
class Solution { public:int findLength(vector<int>& A, vector<int>& B) {vector<int> dp(B.size() + 1, 0);int maxLen = 0;for(int i = A.size() - 1; i >= 0; --i){/* 遍歷順序顛倒 */for(int j = 0; j < B.size(); ++j){/* 相等和不相等兩種情況 */dp[j] = (A[i] == B[j]) ? 1 + dp[j + 1] : 0;maxLen = std::max(maxLen, dp[j]);}}return maxLen;} };為了便于理解,可以
把dp[j]看成dp[i][j],表示當(dāng)前的i當(dāng)前的j
把dp[j+1]看成dp[i+1][j+1],表示上次循環(huán)的i和下次循環(huán)的j
假設(shè)有I1, J1, J11表示某次循環(huán)時(shí)的i,j和j+1,下一輪外層循環(huán)時(shí)i,j和j+1改為I2,J1,J11。
以下稱為第一次和第二次循環(huán)
在第二次循環(huán)過(guò)程中,假設(shè)正要執(zhí)行但還沒(méi)有執(zhí)行
dp[j] = (A[i] == B[j]) ? 1 + dp[j + 1] : 0;寫成上面符號(hào)的格式是
dp[J1] = (A[I2] == B[J1]) ? 1 + dp[J11] : 0;因?yàn)閖是從0到B.size()的,所以只有更新完dp[J1]后才會(huì)更新dp[J11],當(dāng)準(zhǔn)備更新dp[J1]時(shí),dp[J11]還沒(méi)有被更新
沒(méi)有被更新的意思是dp[J11]的值在上次更新后沒(méi)有被改變,上次更新時(shí)的i是I1,j是J11,所以dp[J11] == dp[I1][J11]
因?yàn)閕是從A.size()到0的,所以I1 = I2 + 1,所以
dp[J11] == dp[I1][J11] == dp[I2 + 1][J11]對(duì)于正要更新的dp[J1],此時(shí)的i是I2,所以
dp[J1] == dp[I2][J1]所以上面的更新過(guò)程可以改為
dp[I2][J1] = (A[I2] == B[J1]) ? 1 + dp[I2 + 1][J11] : 0;由于J11 == J1 + 1,所以
dp[I2][J1] = (A[I2] == B[J1]) ? 1 + dp[I2 + 1][J1 + 1] : 0;形式和
dp[i][j] = (A[i] == B[j]) ? 1 + dp[i + 1][j + 1] : 0;相同
總結(jié)
以上是生活随笔為你收集整理的每天一道LeetCode-----计算两个序列最长的公共子序列长度的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 每天一道LeetCode-----计算给
- 下一篇: 每天一道LeetCode-----找到1