動態規劃:洛谷 P2758 編輯距離 —— 一題多解:遞迴和DP求解
阿新 • • 發佈:2022-04-07
洛谷 P2758 編輯距離
這題是普及/提高-的,觀察發現可以用二維陣列DP做。
思路是建一個二維陣列,dp[i][j]代表a的前i個變成b的前j個最少需要的步數。狀態轉移方程:
dp[i][j]=min( min (dp[i-1][j] , dp[i][j-1] )+1, dp[i-1][j-1] + flag) ;
dp[i-1][j]:表示刪。表示的是前i-1個變成j的步數更少,所以不如直接刪掉第i個。
dp[i][j-1]:表示增。表示的是前i個變成前j-1個步數更少,所以不如在i的後面新增一個第j個字元。
dp[i-1][j-1]:表示換。如果第i個等於第j個,那就不用換,直接dp[i][j]=dp[i-1][j-1]+flag,此時flag=1。不然就要換,就需要加一步,dp[i][j]=dp[i-1][j-1]+flag,此時flag=1。
注意點①:邊界條件注意設定,dp[0][j]=j 0個字元變成j個字元要j步,dp[i][0]同理。
注意點②:因為字串是從下標1開始,所以再創兩個char陣列存放陣列,並讓資料從下標1開始,方便後面DP計算。
可以有兩種求解方式:DP和遞迴,這裡的遞迴也可以叫做記憶化搜尋。記憶化搜尋一定能轉化為DP,反之則不行。
一、DP
1 //洛谷 P2758 編輯距離 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 char a[2002]; 6 char b[2002]; 7 stringx1, x2; 8 int dp[2002][2002]; 9 int main() 10 { 11 cin >> x1; 12 cin >> x2; 13 int l1 = x1.size(); 14 int l2 = x2.size(); 15 for (int i = 0; i < l1; ++i) 16 { 17 a[i + 1] = x1[i]; 18 } 19 for (int i = 0; i < l2; ++i) 20 { 21 b[i + 1] = x2[i];22 } 23 for (int i = 1; i <= l1; ++i)dp[i][0] = i;//邊界調節,b陣列長度為0時,變成a陣列要花i步 也就是增添i個 24 for (int j = 1; j <= l2; ++j)dp[0][j] = j; 25 for(int i=1;i<=l1;++i) 26 for (int j = 1; j <= l2; ++j) 27 { 28 int flag = 1; 29 if (a[i] == b[j]) 30 { 31 //如果最後一個元素相同 那麼這一步就不需要加1 32 flag = 0; 33 } 34 dp[i][j] = min(min(dp[i - 1][j], dp[i][j - 1])+1, dp[i - 1][j - 1] + flag); 35 36 } 37 cout << dp[l1][l2]; 38 return 0; 39 }
二、遞迴
//洛谷 P2758 編輯距離 #include<iostream> #include<cstring> using namespace std; char a[2002]; char b[2002]; string x1, x2; int dp[2002][2002]; int recursion(int i, int j)//代表求a的前i個字元變成b的前j個字元需要幾步 { if (dp[i][j] != -1)//如果已經算出來的 直接返回 return dp[i][j]; if (i == 0)//i等於0 需要j步 也是計算邊界條件 return j; if (j == 0) return i; int flag = 1;//判斷最後一個字元相等不 的標誌 if (a[i] == b[j]) flag = 0; return dp[i][j] = min(min(recursion(i - 1, j) + 1, recursion(i, j - 1) + 1), recursion(i - 1, j - 1) + flag); } int main() { cin >> x1; cin >> x2; int l1 = x1.size(); int l2 = x2.size(); for (int i = 0; i < l1; ++i) { a[i + 1] = x1[i]; } for (int i = 0; i < l2; ++i) { b[i + 1] = x2[i]; } memset(dp, -1, sizeof(dp)); int ans = recursion(l1, l2); cout << ans; return 0; }
三、對比時間複雜度
DP:
遞迴:
顯然DP的時間複雜度更小,差了兩倍多,空間複雜度也更省點。