刷题DAY16

题目一

给定两个字符串str1和str2,再给定三个整数ic、dc和rc,分别代表插入、删除和替换一个字符的代价,返回将str1编辑成str2的最小代价。【举例]str1="abc",str2="adc",ic=5,dc=3,rc=2从"abc"编辑成adc",把'b'替换成'd'是代价最小的,所以返回2str1="abc",str2="adc",ic=5,dc=3,rc=100从"abc"编辑成"adc",先删除"b',然后插入'd'是代价最小的,所以返回8str1="abc",str2="abc",ic=5,dc=3,rc=2不用编辑了,本来就是一样的字符串,所以返回0

dp数组解决

首先先想dp[i][j]是是什么意思

i是str1 j是str2 dp[i][j]是 str1的前i位 变成 str2的前j位的代价 那也就是左下角出结果呗

那我们看一下每一个位置的依赖

dp[0][0] 相同代价就是0 要么代价就是一个替换代价

这里一般情况 应该是 添加+删除>替换 代价的 如果添加+删除的代价比替换的代价还小 那完全可以不需要替换这个操作了 但是这种可能性不能不防啊

dp[0][j] 肯定是添加一个的代价

dp[i][0] 那肯定是删除i个的代价

然后常态化的呢? dp[i][j]

如果i和j位置相同的话 那就直接等于dp[i-1][j-1]了 不需要任何新的操作

如果不同 dp[i-1][j-1]+一个替换的代价

或者 dp[i-1][j]+删除代价

就是先把i-1的给弄立整的

比如 abcdf abcd 它的i-1和j是相同的 所以只要删一个就行了

或者 dp[i][j-1]+添加代价

比如 abcd abcdf 那就只要再加一个就行了

(j-1 i-1 并不能说明j-1一定比i小 这里的含义是位置 也就是说j的前一个位置 和i的当前位置相等 甚至再极端一点 i可能是2 j可能是10呢 ab abcdefghij 它的依赖就走了dp[2][9]的值 每个格子只解决相邻的问题就行 虽然这个dp[2][10]的这个子问题并不能直接解决这个问题 甚至可以说与原问题(abcdefghi和abcdefghij)毫无干系 但是它作为可以填满整张表的basecase )

我刚开始是这么写的:

java 复制代码
 public static int MinPath(String str1,String str2,int ic,int dc,int rc) {
           char [] chars1 = str1.toCharArray();
           char [] chars2 = str2.toCharArray();
           int [][] dp = new int [chars1.length][chars2.length]; 
           dp[0][0] = chars1[0]==chars2[0]?0:Math.min(rc,ic+dc);
           for(int i = 1;i<chars1.length;i++) {
        	   dp[i][0] = dp[i-1][0]+dc;
           }
           for(int i = 1;i<chars2.length;i++) {
        	   dp[0][i] = dp[0][i-1]+ic;
           }
           for(int i = 1;i<chars1.length;i++) {
        	   for(int j = 1;j<chars2.length;j++) {
        		   int p1 = dp[i-1][j-1]+rc;
        		   if(chars1[i] == chars2[j]) {
        			   p1 = dp[i-1][j-1];
        		   }
        		   int p2 = Math.min(dp[i-1][j]+dc, dp[i][j-1]+ic);
        		   dp[i][j] = Math.min(p1, p2);
        	   }
           }
           return dp[chars1.length-1][chars2.length-1]; 
	 }

但是在面对

例子的时候其实basecase是错的 当dp[1][2]的时候 chars1[1] chars2[2] 相等所以满足 p1 = dp[i-1][j-1]; dp[0][1] 是什么 a变成fa的最小代价 是先替换一个 再增加一个 这很明显是不对的 我们的最优解应该是 直接加上一个 这种情况是差在哪了呢 差在没考虑当str1为空的时候 比如a f的最佳选择虽然是替换或者删除再添加 那么a->fa的选择 也就被限制在了 先把第一位解决了 再解决后面的

在常态化的时候确实是这个理 但是实际上啊 实际上 我们是忽略掉了 str1a前面的一位 也就是说实际上是可以在a的前面加d 所以实际上我们的dp数组应该大一圈才对

不光是这个原因 还有特殊情况的考虑 如果一个字符串为空呢 对不对 与其搞两个if 不如直接写进dp

java 复制代码
har [] chars1 = str1.toCharArray();
           char [] chars2 = str2.toCharArray();
           int [][] dp = new int [chars1.length][chars2.length]; 
           dp[0][0] = chars1[0]==chars2[0]?0:Math.min(rc,ic+dc);
           for(int i = 1;i<chars1.length;i++) {
        	   dp[i][0] = dp[i-1][0]+dc;
           }
           for(int i = 1;i<chars2.length;i++) {
        	   dp[0][i] = dp[0][i-1]+ic;
           }
           for(int i = 1;i<chars1.length;i++) {
        	   for(int j = 1;j<chars2.length;j++) {
        		   int p1 = dp[i-1][j-1]+rc;
        		   if(chars1[i] == chars2[j]) {
        			   p1 = dp[i-1][j-1];
        		   }
        		   int p2 = Math.min(dp[i-1][j]+dc, dp[i][j-1]+ic);
        		   dp[i][j] = Math.min(p1, p2);
        	   }
           }
           return dp[chars1.length-1][chars2.length-1]; 

另外的 这题每个格子只依赖于 上一个格子或者前一个格子 可以进行优化

而且可以进行 旋转矩阵来保证最优空间(注意转置矩阵后 插入行为和删除行为有变动)

题目二

给定两个单词 word1word2 ,返回使得 word1word2相同 所需的最小步数

每步可以删除任意一个字符串中的一个字符。

刚开始想的是 能不能a 怎么删都变不了 b 结果人家说 a 和 b 全删没就行

java 复制代码
 public int minDistance(String word1, String word2) {
         if(word1==word2) {
			return 0;
		}
		if(word1==null) {
			return word2.length();
		}
		if(word1==null) {
			return word1.length();
		}
       char [] chars1 = word1.toCharArray();
       char [] chars2 = word2.toCharArray();
       int N = chars1.length;
       int M = chars2.length;
       int [][] dp = new int [N][M];
				bo
       for(int i = 0;i<N;i++) {
           dp[i][0] = chars1[i] == chars2[0]?i:-1;
       }
       for(int i = 1;i<M;i++) {
       	dp[0][i] = chars1[0] == chars2[i]?i:-1;
       }
      for(int i = 1;i<N;i++) {
   	   for(int j = 1;j<M;j++) {
          	 if(chars1[i]==chars2[j]) {
          		 if(dp[i][j-1]==-1&&dp[i-1][j]==-1) {
          			 dp[i][j] = i+j;
          		 }else {
          			 dp[i][j]=dp[i-1][j-1];
          		 }
          	 }else {
          		 if(dp[i][j-1]==-1&&dp[i-1][j]==-1) {
          			 dp[i][j] = -1;
          		 }else if(dp[i][j-1]==-1||dp[i-1][j]==-1){
          			 dp[i][j]=dp[i][j-1]==-1?dp[i-1][j]+1:dp[i][j-1]+1;
          		 }else {
          			 dp[i][j]=Math.min(dp[i][j-1], dp[i-1][j])+1;
          		}
          	 }
          }
      }
      return dp[N-1][M-1];   
   }

这是刚开始的做法 寄了

java 复制代码
 public int minDistance2(String word1, String word2) {
      
       char [] chars1 = word1.toCharArray();
       char [] chars2 = word2.toCharArray();
       int N = chars1.length+1;
       int M = chars2.length+1;
       int [][] dp = new int [N][M];

       for(int i = 1;i<N;i++) {
           dp[i][0] = i;
       }
       for(int i = 1;i<M;i++) {
    	   dp[0][i] = i;
       }
      for(int i = 1;i<N;i++) {
   	   for(int j = 1;j<M;j++) {
          	 if(chars1[i-1]==chars2[j-1]) {
          		dp[i][j] = dp[i-1][j-1];
          	 }else {
          		dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1])+1;
          	 }
          }
      }
      return dp[N-1][M-1];   
   }

这个的做法呢 是像上一题一样 多包涵了一圈 也就是加入了字符串为空的可能 所以basecase就有了删除到底的可能性了 然后dp[i][j] 如果字符串1的i位置和字符串2的j位置相等 那就不用删了 他就直接等于dp[i-1][j-1] 要么不等那就是dp[i-1][j]和dp[i][j-1]哪个小 再+1 也就是说 这个删除要么是str1删掉一个 要么是str2删掉一个 每次以1为步过 不用不相同时判断dp[i-1][j-1]的情况 因为已经包含在dp[i-1][j]和dp[i][j-1]的判断中了

看人家的题解说是 最长公共子序列 有道理 把最长公共子序列的剩余部分删掉就是结果了

相关推荐
前端Hardy4 分钟前
HTML&CSS: 实现可爱的冰墩墩
前端·javascript·css·html·css3
2401_857439699 分钟前
SpringBoot框架在资产管理中的应用
java·spring boot·后端
怀旧66611 分钟前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
李老头探索13 分钟前
Java面试之Java中实现多线程有几种方法
java·开发语言·面试
粤海科技君17 分钟前
如何使用腾讯云GPU云服务器自建一个简单的类似ChatGPT、Kimi的会话机器人
服务器·chatgpt·机器人·腾讯云
芒果披萨18 分钟前
Filter和Listener
java·filter
qq_49244844623 分钟前
Java实现App自动化(Appium Demo)
java
傲骄鹿先生27 分钟前
阿里云centos7.9服务器磁盘挂载,切换服务路径
服务器·阿里云·磁盘
阿华的代码王国31 分钟前
【SpringMVC】——Cookie和Session机制
java·后端·spring·cookie·session·会话
web Rookie34 分钟前
JS类型检测大全:从零基础到高级应用
开发语言·前端·javascript