代码随想录-Day54~58-动态规划|编辑距离-

继续补作业~~~

72. 编辑距离

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数  。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

核心思路:还是一道经典的动态规划问题,需要明确的是dp数组含义以i-1字符为结尾的word1和以j-1结尾的字符word2替换成功所使用的最小操作数为dp[i][j]

明确递推公式,存在两种情况

情况一:两个字符相等,相等就不需要进行操作,也就是继承上一个状态就ok

情况二:两个字符不相等,不相等我们需要执行插入,删除,或者替换,删除一个字符有可能操作的是word1也有可能是word2,所以此时的 dp[i][j]有可能是dp[i][j-1]也有可能是dp[i-1][j],并且删除字符之后操作数需要加1,其实插入也是变相的删除,所以合并为一个

之后需要讨论替换的状态,明确替换之后操作数加一,并且替换之后两个字符相等,也就是达到状态dp[i-1][j-1]如此 递推公式就确定了,之后初始化的时候根据含义第一行第一列依旧和字符数量相等。

完整代码如下

class Solution {
    public int minDistance(String word1, String word2) {
        //使用动态规划解决
        //1,确定dp、数组
        //dp[i][j]代表以i-1结尾的word1和以j-1结尾的word2相同时的最小操作数
        int len1=word1.length();
        int len2=word2.length();
        int[][] dp=new int[len1+1][len2+1];
        //2,确定递推公式
        //对于本题目来说,我们需要考虑两种情况,一种是两个字符相等,一种是不相等
        //当两个字符相等的时候,不需要操作所以dp[i][j]=dp[i-1][j-1]
        //当两个字符不相等,有三种操作方式,插入,删除,替换
        //1,删除
        //删除一个字符之后操作数加1,可能删除word1也可能word2,对应
        //dp[i][j]=dp[i-1][j]+1
        //dp[i][j]=dp[i][j-1]+1
        //2,插入
        //其实插入的逻辑就是反向的删除,所以 不需要格外考虑
        //3,替换
        //替换一个之后两个字符就可以达到相等的状态,并且此时操作数需要加1
        //dp[i][j]=dp[i-1][j-1]+1
        //初始化
        for(int i=0;i<=len1;i++) dp[i][0]=i;
        for(int j=0;j<=len2;j++) dp[0][j]=j;
        //遍历
        for(int i=1;i<=len1;i++){
            for(int j=1;j<=len2;j++){
                if(word1.charAt(i-1)==word2.charAt(j-1)){
                    dp[i][j]=dp[i-1][j-1];
                }else{
                    dp[i][j]=Math.min(dp[i-1][j-1]+1,
                    Math.min(dp[i-1][j]+1,dp[i][j-1]+1));
                }
            }
        }
        return dp[len1][len2];
    }
}

647. 回文子串

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

整体思路:依旧使用动态规划的思路,虽然本题判断的是一个字符串以及子串,但是我们使用二位dp数组 dp[i][j]代表的是在区间 [i,j]的字符是否是回文子串,我们dp数组并不记录数量,为了后续推导我们定义为一个boolean类型的数组

这时候需要推导递推公式,其实也是两种情况

情况一:两个字符相等

如果两个字符相等其实有三种情况第一种类似于   a  只有一个字符,第二种 aa 两个字符,这两种都可以归为一类,不影响,但是还有一种情况类似于  a***a你并不知道中间的情况,所以就需要进行对中间是否是会问的判断,如果是会文串,个数加一,如果不是不需要讨论。

完整代码如下:

class Solution {
    public int countSubstrings(String s) {
        //动态规划
        //1,dp数组含义
        //dp[i][j]表示在全范围内[i.j]的子串是否是回文子串
        int len=s.length();
        boolean[][] dp=new boolean[len][len];
        //2,确定递推公式
        //当两个字符相等的时候,有三种情况
        //1,i=j,直观表达  a
        //2             aa
        //3,        a***a
        //对于这三种情况,只有第三种需要关心[i-1,j-1]是否相等
        //3,初始化,默认为false
        //4,遍历顺序,因为dp[i][j]可能是由dp[i+1][j-1]推导而来,
        //所以应该从下往上,从左往右
        int res=0;
        for(int i=len-1;i>=0;i--){
            for(int j=i;j

516. 最长回文子序列

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

示例 1:

输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 

解题思路:这道题的思路和上一道差不多,主要是最长序列的定义是可以不连续,所以讨论的时候需要注意这种情况

首先确定dp数组含义此时的dp数组含义就是区间[i,j]对应的字符的最长回文子串的最大长度,确定递推公式,也存在两种情况,一种是两个字符相等很自然的回文串长度加2,问题是如果相等,则存在两种情况,一种是取左边字符的转态,另一种是右边字符状态,最后比较,其实这里我个人认为是比较抽象的,后续再思考思考

完整代码:

class Solution {
    public int longestPalindromeSubseq(String s) {
        //动态规划
        //1,确定dp数组含义
        //dp[i][j] 字符串在[i,j]范围内的最长回文子序列为dp[i][j]
        int len=s.length();
        int[][] dp=new int[len][len];
        //2,确定递推公式
        //情况一,当两个字符相等的时候,子串的长度应该加2
        //dp[i][j]=dp[i-1][j-1]+2;
        //情况二,两个字符不相等的时候,说明同时加入并不能增加区间回文子串的长度
        //于是,尝试分别加入
        //dp[i][j]=max(dp[i][j-1],dp[i+1][j])
        //3,dp数组初始化
        //当两个字符相等的时候回文子串为1
        for(int i=0;i=0;i--){
            for(int j=i+1;j

739. 每日温度

给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

示例 1:

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]j

解题思路:其实一开始我是使用暴力算法求解了,但是不出意外的是超时了,分析了超时愿意就是重复的遍历已经遍历过的元素

于是使用单调栈来处理这个问题,思路想明白了,直接上代码:

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
       //使用暴力解法超时,接下来使用单调栈解决
       //单调栈顾名思义是存放的单调元素,在本题中,我们存放便利过的数组
       //的下表,这样在下一次便利的时候就不需要在进行额外的遍历
       int len=temperatures.length;
       int[] res=new int[len];
       Deque stack=new LinkedList();
       stack.push(0);//现将第一个元素入栈
       for(int i=1;itemperatures[stack.peek()]){
                   res[stack.peek()]=i-stack.peek();
                   stack.pop();
               }
               stack.push(i);
           }
       }
       return res;
    }
    //暴力解法,超时了
    public int[] baoli(int[] temperatures){
         //双重for循环解决,思路没问题,但是超时了,潮湿的原因就是
        //已经计算过的下标被重复的计算,例如上一次我从1开始计算到了下表7,这一次
        //我继续从2开始,2~7
        int len=temperatures.length;
        int[] index=new int[len];
        a:for(int i=0;itemperatures[i]){
                    res++;
                    index[i]=res;
                    break b;//结束第二层也就是for循环b
                }else{
                    res++;
                }
            }
           
        }
        return index;
    }
}

                                                                                                                        By-三条直线围墙

你可能感兴趣的:(动态规划,算法)