题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2476
题意:给出两个字符串str1, str2,每次操作都可以将任意区间所有字符改成某个字符,问最少需要几次操作可以将字符串str1变成str2;
思路:区间dp
写过一道和本题很像的题目:http://www.cnblogs.com/geloutingyu/p/6597111.html
这道题求的是将一个空串变成目标串最少需要几次操作;
对于本题,不妨先求出由空串变成str2至少需要几次操作,再拿str1去匹配。
假设已经求得dp[i][j]为空串变成str[i, j]需要的最少操作次数(关于如何求dp可以看我前面给出的那篇博客);
用ans[i]存储区间[0, i]中str1变成str2最少需要的操作次数,显然初始化为ans[i]=dp[0][i],str1变成str2需要的操作肯定不会比空串多;
对于当前字符,若str1[i]==str2[i],那么当前字符不需要再涂抹,所以有ans[i]=ans[i-1];
若str1[i]!=str2[i],区间[0, i]的子串中可能有匹配的字符,那么可以用其子串来更新当前串的解,所以可以枚举一个中间字符 j,则有:
ans[i]=min(ans[i], ans[j]+dp[j+1][i])
代码:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define MAXN 110 5 using namespace std; 6 7 char str1[MAXN], str2[MAXN]; 8 int dp[MAXN][MAXN];//dp[i][j]存储从空白得到str2[i,j]至少要涂抹多少次 9 int ans[MAXN];//ans[1]存储区间[0,i]str1,str2匹配最少需要涂抹的次数 10 11 int main(void){ 12 while(~scanf("%s%s", str1, str2)){ 13 memset(dp, 0, sizeof(dp)); 14 memset(ans, 0, sizeof(ans)); 15 int len=strlen(str2); 16 for(int i=0; i<len; i++){//i为尾字符 17 for(int j=i; j>=0; j--){//i为尾字符,j为头字符 18 dp[j][i]=dp[j+1][i]+1;//若没有重复的字符,那么[i,j]可以由[i+1,j]+1得到 19 for(int k=j+1; k<=i; k++){ 20 if(str2[k]==str2[j]){//枚举中间字符,若str2[k]==str2[j],则dp[i][k]==dp[i+1][k] 21 dp[j][i]=min(dp[j][i], dp[j+1][k]+dp[k+1][i]); 22 } 23 } 24 } 25 } 26 for(int i=0; i<len; i++){//初始化ans数组 27 ans[i]=dp[0][i]; 28 } 29 for(int i=0; i<len; i++){ 30 if(str2[i]==str1[i]){//若当前字符相同,则不需要涂抹 31 if(!i){ 32 ans[i]=0; 33 }else{ 34 ans[i]=ans[i-1]; 35 } 36 }else{//若前字符不同则通过其子串更新当前字符串使解最优 37 for(int j=0; j<=i; j++){ 38 ans[i]=min(ans[i], ans[j]+dp[j+1][i]); 39 } 40 } 41 } 42 printf("%d ", ans[len-1]); 43 } 44 return 0; 45 }