华为校招第三题:字符串变换最小费用(动态规划DP问题)

题目:
给出两个字串A,B。将A字串转化为B字串,转化一共有两种方式:删除连续的n个字符,一次操作费用为2。增加连续的n个字符(增加的字符是什么由你决定),一次操作费用为n+2。求把A变为B最小费用。

输入: 
第一行输入一个正整数T(1 <= T <= 10),表示有T组测试数据。
对于每组测试数据:
两行字符串A, B(字符串长度不超过2000,字符仅包含小写字母)
输出: 
对于每组测试数据,输出一行一个整数,表示最小费用。
样例输入:   
2
dsafsadfadf
fdfd
aaaaaaaa
bbbbbbbb
样例输出:   
7
12

答案提示:
“dsafsadfadf” 变成 “fdfd” 最少的代价的一种方法是:
1. “dsafsadfadf” -> “f” 删除连续的10个,代价2
2. “f” -> “fdfd” 增加连续的3个(”dfd”),代价为3 + 2 = 5
总共的最小代价为2 + 5 = 7,其他方法的代价都不小于7
“aaaaaaaa” 变成 “bbbbbbbb” 最小代价的一种方法是:
1. “aaaaaaaa” 全部删除,代价2
2. 增加8个连续的’b’,代价10
总共的最小代价为2 + 10 = 12
注意,有些最优的方案可能要做多次的删除和增加操作,不限于两次。

这是一个比较典型的动态规划的题目:

public class Main {
            public static void main(String[] args) {  
               Scanner sc=new Scanner(System.in);
               int a=sc.nextInt();
               String[] buff=new String[a*2];
               int i=0;
               sc.nextLine();  //为了清除到 nextInt截取之后的空格
               //sc.next()方法是以换行或者空格符为分界线接收下一个String类型变量
               while(true)
               {
                   buff[i]=sc.nextLine();
                   System.out.println("调试: "+buff[i]);
                   i+=1;
                   if(a*2==i) break;
               }
               for(int j=0;jout.println(GetMinExpenses(buff[j*2],buff[j*2+1]));
               }


            }  

               public static int DEL = -1;  

               public static int ORIGAL = 0;  

               public static int ADD = 1;  

               public static int getAddCount(int f, int type) {  
                   int minCost;  
                   if (type == ADD) {  
                       minCost = f + 1;  
                   } else {  
                       minCost = f + 3;  
                   }  
                   return minCost;  
               }  

               public static int getDelCount(int f, int type) {  
                   int minCost = 0;  
                   if (type == DEL) {  
                       minCost = f + 0;  
                   } else {  
                       minCost = f + 2;  
                   }  
                   return minCost;  
               }  

               public static int getMin(int a, int b) {  
                   return a < b ? a : b;  
               }  

               public static int GetMinExpenses(String aString, String bString) {  
                   int[][] f = new int[aString.length() + 1][bString.length() + 1];//f[i][j] 从a[i] -> b[j]的最小代价  
                   f[0][0] = 0;  
                   int operator[][] = new int[aString.length() + 1][bString.length() + 1];//用于记录操作  

                   for (int i = 1; i < aString.length() + 1; i++) {  
                       f[i][0] = 2;  
                       operator[i][0] = DEL;  
                   }  
                   for (int i = 1; i < bString.length() + 1; i++) {  
                       f[0][i] = 2 + i;  
                       operator[0][i] = ADD;  
                   }  
                   int type = ORIGAL;  
                   for (int i = 1; i < aString.length() + 1; i++) {  
                       // System.out.println();  
                       for (int j = 1; j < bString.length() + 1; j++) {  
                           // System.out.print(minCost + "_" + operator[i][j] + " ");  
                           int tempType;  
                           int cost = 0;  
                           if (aString.charAt(i - 1) != bString.charAt(j - 1))  
                           {  
                               cost = 5;  
                           }  
                           int minCost;  
                           int delCount = getDelCount(f[i - 1][j], operator[i - 1][j]);  
                           /* 
                            * 
                            *          // getDelCount(f,type)
                                   if (type == DEL) {  
                                        minCost = f + 0;  
                                    } else {  
                                        minCost = f + 2;  
                                    }   

                                    // 看她 前面的 是不是 DEL,如果是,那个再把这个字母代价 删到 ,需要增加的代价就为 0
                           */

                           int addCount = getAddCount(f[i][j - 1], operator[i][j - 1]);  
                           /*
                            *    //getAddCount(f,type)
                                if (type == ADD) {  
                                    minCost = f + 1;  
                                } else {  
                                    minCost = f + 3;  
                                }  
                             */   

                           if (delCount >= addCount)  
                           {  
                               operator[i][j] = ADD;  
                               minCost = addCount;  
                           }  
                           else  
                           {  
                               operator[i][j] = DEL;  
                               minCost = delCount;  
                           }  
                           if (minCost > f[i - 1][j - 1] + cost)  
                           {  
                               operator[i][j] = ORIGAL;  
                               minCost = f[i - 1][j - 1] + cost;  
                           }  
                           f[i][j] = minCost;  
                       }  
                       // System.out.println("");  
                   }  
                   return f[aString.length()][bString.length()];  
               }  


            }

在做动态规划的题目的时候,需要明白自己需要建几张二维表,以及二维表的实际物理意义。将原文题,递归到子问题。这样可以写出递归方程。
数据结构 和 递归方程便是程序的两个重要部分。
如代码所示: f[i][j]表示 从a[i] -> b[j]的最小代价,以 “dsafsadfadf” 变成 “fdfd” 为例:
f的数组的内容如下:

j\i f d f d
1 2 3 4
0 0 0 3 4 5 6
1 d 2 5 3 6 6
2 s 2
3 a 2
4 f 2
5 s 2
6 a 2
7 d 2
8 f 2
9 a 2
10 d 2
11 f 2

operate数组内容如下:

j\i f d f d
1 2 3 4
0 0 ADD ADD ADD ADD
1 d DEL ADD ORIGAL ADD
2 s DEL
3 a DEL
4 f DEL
5 s DEL
6 a DEL
7 d DEL
8 f DEL
9 a DEL
10 d DEL
11 f DEL

cost=5 为一个常量,含义是由A 到B 如果两个字符不等。那么他所消耗的代价就为5。(删除一个代价为2,增加一个为5) 如果字符相等,那么他的代价就为0.
结合代码 以d到f 为例。有d 到f 有三种路径可以走。

    初始条件: 由d  到0  ,消耗的代价为 2
                      由 0到 f   ,消耗的代价为 3

考虑问题的递归性:
1.先删除d,那么需要进行add f 操作,得到 f : 代价为 2+3
2.先增加f,那么需要进行DEL d 操作,得到f: 代价为 3+2
在这两个操作的时候考虑到递推问题的上一个状态,她的前一状态是 DEL,还是ADD
3.第三种状态就是,不管子问题,就是直接删除一个,加一个,为5.

从左到右为 ADD状态,从上到下为DEL状态。ORIGAL,为对角线过来的数据,也就是说,不考虑这个字母的前一个状态。

递归方程如下:
华为校招第三题:字符串变换最小费用(动态规划DP问题)_第1张图片
最右下角的值,就是我们要求得最小代价。

你可能感兴趣的:(面试题)