一.最長公共子序列問題(LCS問題)算法
給定兩個字符串A和B,長度分別爲m和n,要求找出它們最長的公共子序列,並返回其長度。例如:數組
A = "HelloWorld"app
B = "loop"優化
則A與B的最長公共子序列爲 "loo",返回的長度爲3。此處只給出動態規劃的解法:定義子問題dp[i][j]爲字符串A的第一個字符到第 i 個字符串和字符串B的第一個字符到第 j 個字符的最長公共子序列,如A爲「app」,B爲「apple」,dp[2][3]表示 「ap」 和 「app」 的最長公共字串。注意到代碼中 dp 的大小爲 (n + 1) x (m + 1) ,這多出來的一行和一列是第 0 行和第 0 列,初始化爲 0,表示空字符串和另外一字符串的子串的最長公共子序列,例如dp[0][3]表示 "" 和 「app」 的最長公共子串。spa
class LCS { public: int findLCS(string A, int n, string B, int m) { if(n == 0 || m == 0)//特殊輸入 return 0; int dp[n + 1][m + 1];//定義狀態數組 for(int i = 0 ; i <= n; i++)//初始狀態 dp[i][0] = 0; for(int i = 0; i <= m; i++) dp[0][i] = 0; for(int i = 1; i <= n; i++) for(int j = 1; j<= m; j++) { if(A[i - 1] == B[j - 1])//判斷A的第i個字符和B的第j個字符是否相同 dp[i][j] = dp[i -1][j - 1] + 1; else dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]); } return dp[n][m];//最終的返回結果就是dp[n][m] } };
該算法的時間複雜度爲O(n*m),空間複雜度爲O(n*m)。此外,因爲遍歷時是從下標1開始的,由於下標爲0表示空字符串;因此第A的第i個字符實際上爲A[i -1],B的第j個字符爲B[j-1]。.net
二.最長公共子串問題code
給定兩個字符串A和B,長度分別爲m和n,要求找出它們最長的公共子串,並返回其長度。例如:blog
A = "HelloWorld"字符串
B = "loop"get
則A與B的最長公共子串爲 "lo",返回的長度爲2。咱們能夠看到子序列和子串的區別:子序列和子串都是字符集合的子集,可是子序列不必定連續,可是子串必定是連續的。一樣地,這裏只給出動態規劃的解法:定義dp[i][j]表示以A中第i個字符結尾的子串和B中第j個字符結尾的子串的的最大公共子串(公共子串實際上指的是這兩個子串的全部部分)的長度(要注意這裏和LCS的不一樣,LCS中的dp[i+1][j+1]必定是大於等於dp[i][j]的;但最長公共子串問題就不必定了,它的dp[i][j]表示的子串不必定是以A[0]開頭B[0]開頭的,可是必定是以A[i-1]、B[j-1]結尾的),一樣地, dp 的大小也爲 (n + 1) x (m + 1) ,這多出來的一行和一列是第 0 行和第 0 列,初始化爲 0,表示空字符串和另外一字符串的子串的最長公共子串。
當咱們要求dp[i][j],咱們要先判斷A的第i個元素B的第j個元素是否相同即判斷A[i - 1]和 B[j -1]是否相同,若是相同它就是dp[i - 1][j- 1] + 1,至關於在兩個字符串都去掉一個字符時的最長公共子串再加 1;不然最長公共子串取0。因此整個問題的初始狀態爲:
$$ dp[i][0] =0 , dp[0][j] = 0$$
class LongestSubstring { public: int findLongest(string A, int n, string B, int m) { if(n == 0 || m == 0) return 0; int rs = 0; int dp[n + 1][m + 1]; for(int i = 0 ; i <= n; i++)//初始狀態 dp[i][0] = 0; for(int i = 0; i <= m; i++) dp[0][i] = 0; for(int i = 1; i <= n; i++) for(int j = 1; j<= m; j++) { if(A[i - 1] == B[j - 1]) { dp[i][j] = dp[i -1][j - 1] + 1; rs = max(rs,dp[i][j]);//每次更新記錄最大值 } else//不相等的狀況 dp[i][j] = 0; } return rs;//返回的結果爲rs } };
該算法的時間複雜度爲O(n*m),空間複雜度爲O(n*m)。一樣地,遍歷下標也是從1開始的。不過關於最長公共子串問題,有幾點須要注意下:
1.因爲dp[i][j]不像LCS是個遞增的數組,因此它在每次更新時須要同時更新最大值rs,且最後返回的結果是rs。而LCS中返回的直接就是dp[n][m]。
2.從代碼上來看,二者的結構其實差很少,只不過狀態轉移方程有些小許的不一樣,分析過程也相似。
3.另外,關於這量兩種問題還有更優的解法,不過本文主要是DP的思想去解決,固然其中還有對DP的優化,不過此處再也不詳述。
參考:https://www.nowcoder.com/questionTerminal/c996bbb77dd447d681ec6907ccfb488a