经典的动态规划问题:
题目描述:给定两个字符串str1,长度为n和str2,长度为m,再给定三个整数ic,dc,rc,分别代表插入,删除和替换一个字符的代价。返回将str1编辑成str2的最小代价。
比如:str1=“abc”,str2=“adc”,ic=5,dc=3,rc=2,从“abc”编辑成“adc”,把‘b’替换成‘d’的代价是最小的,所以返回2。再比如rc=100的话,先删除‘b’,然后插入‘d’是代价最小的,所以返回8。
动态规划的一般解法,一个矩阵。而关键在于,矩阵每个值所代表的含义。本题中建立矩阵dp[n+1][m+1]。dp[i][j]代表str1[0~i-1]编辑成str2[0~j-1]的最小代价。为什么是这样一个含义?因为字符串转成字符数组,下标从0开始,所以第i个字符对应到str1上为第i-1个字符。而dp[i][j]的值根据最后一个字符的操作不确定,str1[i]与str2[j]如果不相等,就意味着会有替换这种操作,如果二者相等意味着不需要进行操作,它的值就是dp[i-1][j-1]而两个字符串的长度差异决定了删除和插入的操作。
dp[i][j]有以下四种可能的值:
当str1需要删除一个字符才能变成str2时,这时dp[i][j]的值(不要忘了dp[i][j]代表着str1[0~i-1]变成str2[0~j-1]的最小代价)为dp[i-1][j]+dc。就是说str1[0~i-2]变成str2[0~j-1]的最小代价加上最后一个删除第i-1个字符的代价,最终的代价为dp[i][j]的值。
同理当str1需要插入一个字符才能变成str2时,这时dp[i][j]的值为dp[i][j-1]+ic。就是说str1[0~i-1]只能变成str2[0~j-2],最后一个需要插入。
前两种情况为两个字符串长度不等时的变换代价,当两个字符串长度相等时,就需要看最后一个字符是否相等。当最后一个字符相等时也就是str1[i-1]=str2[j-1]时,dp[i][j]就等于dp[i-1][j-1],就是说最后一步不需要做什么。
当最后一个字符不相等时str1[i-1]!=str2[j-1],dp[i][j]就等于dp[i-1][j-1]+rc的值。就是说最后一个字符需要进行替换。
以上四种值,具体是哪个,哪个最小选择哪个。然后对于矩阵而言从上到下从左到右依次计算即可。
和之前一样,用实际的例子演示。str1=“ab12cd3”,str2=“abcdf”,插入代价为:5,删除代价为:3,替换代价为:2。生成矩阵dp[8][6]。
动态规划的矩阵最重要的通常就是第一行和第一列了。这里讲0位置作为空,第一列为从str1[0~i-1]变成空的代价,就是每一个字符都删除的代价,为dc*i。
第一行为由空字符变成str2[0~j-1]的过程,就是都插入的代价,为ic*i。接着剩下的就非常好计算了。
空 | a | b | c | d | f | ||
---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | ||
空 | 0 | 0 | 5 | 10 | 15 | 20 | 25 |
a | 1 | 3 | 0 | 5 | 10 | 15 | 20 |
b | 2 | 6 | 3 | 0 | 5 | 10 | 15 |
1 | 3 | 9 | 6 | 3 | 2 | 7 | 12 |
2 | 4 | 12 | 9 | 6 | 5 | 4 | 9 |
c | 5 | 15 | 12 | 9 | 6 | 7 | 6 |
d | 6 | 18 | 15 | 12 | 9 | 6 | 9 |
3 | 7 | 21 | 18 | 15 | 12 | 9 | 8 |
下面为代码:
public int findMinCost(String A,int n,String B,int m,int c0,int c1,int c2){
if(A == null || B == null){
return 0;
}
char[] str1 = A.toCharArray();
char[] str2 = B.toCharArray();
int[][] dp = new int[n+1][m+1];
for(int i = 0;i < n+1;i++){
dp[i][0] = i * c1;
}
for(int i = 0;i < m+1;i++){
dp[0][i] = i * c0;
}
for(int i = 1;i < n+1;i++){
for(int j = 1;j < m+1;j++){
int temp1 = 0;
if(str1[i-1] != str2[j-1]){
temp1 = dp[i-1][j-1] + c2;
}else{
temp1 = dp[i-1][j-1];
}
dp[i][j] = Math.min(Math.min(dp[i][j-1]+c0, dp[i-1][j]+c1), temp1);
}
}
return dp[n][m];
}