給定了兩個長度為\(n,m\)的序列,找出他們的最長公共子串行,要求嚴格上公升,只需要長度.例如兩個序列分別是1 1 2 3
和1 2 3
那麼答案是1 2 3
長度為3.
資料範圍:\(1 \leq n,m \leq 3000\)
顯然從複雜度上來說這個題要求的是乙個\(o(n^2)\)的解法.那麼由於這個題本身是來自兩個經典模型lcs和lis拼接過來的,那麼比較容易的就可以想到這個題所需的幾個關鍵資訊:兩個序列分別走到哪個位置以及當前末尾的是誰.
狀態定義:\(f[i][j]\)表示當前a序列用到了\([1,i]\)而b序列用到了\([1,j]\)且以\(b[j]\)作為末尾元素的lcis的所有方案中長度最大值.
入口:全是\(0\),方便起見把\(a[0],b[0]\)記作\(-inf\).
轉移:可以模擬原來的兩個模型得到:
出口:\(\max_f[n][i]\)
那麼上述的狀態計算可以以\(o(n^3)\)的暴力遞推求解.但這顯然不夠,還得要把這個複雜度降掉一維,或者至少也要降乙個\(log\).那麼考慮如何降掉一維,乙個乙個來看的話,首先列舉\(i,j\)肯定是避無可避的,想要降只能動這個\(k\).聯絡到之前的乙個轉換,即\(b[k]=a[i]\)這一步,不妨把\(j\)進行遞推轉移的時候,看做\(i\)是乙個常數,也就是\(a[i]\)是乙個常數,現在就不看\(i\)是個什麼情況只看\(j\)了.那麼狀態轉移方程在這一維就是\(f[j] = \max_f[k]+1\),可以注意到很關鍵的一點,那就是既然這個條件是乙個小於常數且只取所有滿足條件的值裡的最大值,那麼不妨就把之前可以拿來轉移的最大值記錄下來,進而可以觀察到這個式子的乙個特點就是當\(j\)增大的時候,\(k\)的決策集合只會增加乙個數,就是說當\(j\)增大的時候,所有\(k\)的取值範圍裡才可能增加乙個,這是乙個"決策集合裡的元素只增加不減少"的情況,而且只會選取裡面的最大值.因此可以從最開始記錄乙個最大值,每個\(j\)增加的時候嘗試把當前乙個新的\(k\)放進決策集合,並嘗試更新最大值,如果需要最大值,那麼就直接拿過來用就可以了.由此就可以把整個題的複雜度降到\(o(n^2)\).
在最開始的時候,最大值應該設定成\(1\).因為開始的時候對應過去的元素應該是\(b[0]\),而他顯然是滿足\(b[0]的.
#include #include using namespace std;
const int n = 3005;
int a[n],b[n],f[n][n];
int main()
}int res = 0;
for(int i = 1;i <= n;++i) res = max(res,f[n][i]);
cout << res;
return 0;
}
最長公共上公升子串行
題目描述 給定兩個整數序列,求它們的最長上公升公共子串行。輸入描述 輸入兩組資料,每組資料代表乙個整數序列,其輸入格式為 第一行輸入長度m 1 m 500 第二行輸入該序列的m個整數ai 231 ai 231 輸出描述 輸出共兩行。第一行輸出兩個序列的最長上公升公共子串行的長度l 第二行輸出該子串行...
最長公共上公升子串行
首先,在 a i b j 的時候有 dp i j dp i 1 j 為什麼呢?因為 dp i j 是以b j 為結尾的 lcia 如果dp i j 0 那麼就說明 a 1 a i 中必然有乙個字元 a k 等於b j 如果dp i j 等於0 呢?那賦值與否都沒有什麼影響了 因為 a k a i 那...
最長公共(上公升)子串行
題目 common subsequence 題目描述 給定兩個字串a和b 或數字序列 求乙個字串,使得這個字串是所給兩個字串的最長公共部分 可以不連續 動態規劃的做法 時間複雜度,o n m include include include include using namespace std co...