题目
分析
根据最长公共子序列中状态表示的技巧,本题依旧两个串。状态标识设 dp[i,j] 表示将a[1-i] 变为 b[1-j] 的所有操作方式的集合中最少操作次数。
其实做了几道线性dp的题目,发现还是有些技巧:就是大都根据最后一个状态或者最后一个或一对元素考虑
递推公式的推导:考虑元素a[i] 和 b[j]的三种操作
1. 通过删除a[i] 使得 a[1-i] 变为 b[1-j],意味着a[1-i-1]与b[1-j]已经匹配 。
dp[i,j] = dp[i-1,j] + 1
2. 通过对a数组增加元素使得 a[1-i+1] 变为 b[1-j],意味着a[1-i]与b[1-j-1]已经匹配
dp[i,j] = dp[i,j-1] + 1
3.通过修改a[i]使得 a[1-i] 变为 b[1-j],意味着[1-i-1]与b[1-j-1]已经匹配
dp[i,j] = dp[i,j-1] + (1/0) 若a[i] == b[j] 则不用加1,若不等则需要改a[i],加1
最终 dp[i][j] = min(dp[i-1,j] + 1, dp[i,j-1] + 1,dp[i,j-1] + (1/0)
代码
1 #include<iostream> 2 #include<algorithm> 3 #include<climits> 4 using namespace std; 5 6 const int N = 1010; 7 char a[N],b[N]; 8 int dp[N][N]; 9 int main(){ 10 int n,m; 11 scanf("%d%s",&n,a+1); 12 scanf("%d%s",&m,b+1); 13 //边界初始化 14 //若a数组0个元素,则a只能添加操作 15 for(int i = 0;i <= m;i++) dp[0][i] = i; 16 //若b数组0个元素,则a只能删除操作 17 for(int i = 0;i <= n;i++) dp[i][0] = i; 18 19 for(int i = 1;i <= n;i++){ 20 for(int j = 1;j <= m;j++){ 21 dp[i][j] = min(dp[i-1][j],dp[i][j-1]) + 1; 22 if(a[i] == b[j]){ 23 dp[i][j] = min(dp[i][j],dp[i-1][j-1]); 24 } 25 else{ 26 dp[i][j] = min(dp[i][j],dp[i-1][j-1]+1); 27 } 28 29 } 30 } 31 printf("%d",dp[n][m]); 32 return 0; 33 }
注意边界条件!!!第0行或第0列并不一定全为0!!!
时间复杂度O(N2)