当前位置:   article > 正文

数据结构与算法---动态规划---编辑距离、最长回文子串_编辑距离 子串

编辑距离 子串

编辑距离

72. 编辑距离
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

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

插入一个字符
删除一个字符
替换一个字符
  • 1
  • 2
  • 3

示例 1:

输入:word1 = “horse”, word2 = “ros”
输出:3
解释:
horse -> rorse (将 ‘h’ 替换为 ‘r’)
rorse -> rose (删除 ‘r’)
rose -> ros (删除 ‘e’)

示例 2:

输入:word1 = “intention”, word2 = “execution”
输出:5
解释:
intention -> inention (删除 ‘t’)
inention -> enention (将 ‘i’ 替换为 ‘e’)
enention -> exention (将 ‘n’ 替换为 ‘x’)
exention -> exection (将 ‘n’ 替换为 ‘c’)
exection -> execution (插入 ‘u’)

提示:

0 <= word1.length, word2.length <= 500
word1 和 word2 由小写英文字母组成
  • 1
  • 2

思路

还是按照DP三部曲来判断

是不是最值问题?

最少操作步骤,是最值问题

能否使用DP?
  1. 原问题是否能分解为子问题?
  2. 有无后效性?
使用DP步骤?
  1. 确定DP含义
  2. 确定边界值
  3. 确定状态转移方程

给定一个单词,转换为另外一个单词
最后一步只有上面三种情况
假设,前面的工作都做好,最后一步是插入一个字符
假设,前面的工作都做好,最后一步是删除一个字符
假设,前面的工作都做好,最后一步是替换一个字符

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
    public int minDistance(String word1, String word2) {
        //边界值
        if(word1 == null || word2 == null) return 0;

        char[] word1CharArray = word1.toCharArray();
        char[] word2CharArray = word2.toCharArray();
        
        int word1Length = word1CharArray.length;
        int word2Length = word2CharArray.length;

        //dp的长度为word1的长度+1
        int[][] dp = new int[word1Length + 1][word2Length + 1];
        dp[0][0] = 0;

        //行为0
        for(int col = 1; col <= word2Length; col++){
            dp[0][col] = col;
        }

        //列为0
        for(int row = 1; row <= word1Length; row++){
            dp[row][0] = row;
        }

        for(int row = 1; row <= word1Length; row++){
            for(int col = 1; col <= word2Length; col++){
                //从上到下
                int topToBottom = dp[row - 1][col] + 1;

                //从左到右
                int leftToRight = dp[row][col - 1] + 1;

                //从左上到右下
                int leftTopToRightBottom = dp[row - 1][col - 1] + 1;
                if(word1CharArray[row - 1] == word2CharArray[col - 1]){
                    leftTopToRightBottom = dp[row - 1][col - 1];
                }

                dp[row][col] = Math.min(Math.min(topToBottom, leftToRight), leftTopToRightBottom);
            }
        }

        return dp[word1Length][word2Length];
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

注意
两层for循环的时候,还是从1开始,而不是从2开始,因为dp[1][1]前面是没有求的


最长回文子串

5. 最长回文子串

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。

示例 2:

输入: “cbbd”
输出: “bb”


思路

如何判断一个字符串是回文呢?

需要借助两个指针,左边一个指针,右边一个指针。判断两个值是否相等,相等则做相应的加法与减法,不相等则不是回文子串

思路1:暴力法

两个for循环,时间复杂度为O(n^2)
每一个区间都需要判断是否是回文子串,都需要O(n)
因此,整体时间复杂度为O(n^3)

class Solution {
    public String longestPalindrome(String s) {
        //边界值
        if(s == null) return null;
        if(s.length() == 0) return "";

        char[] sArray = s.toCharArray();
        ArrayList list = new ArrayList();
        int max = 0;

        int resultLeft = 0;
        int resultRight = 0;
        for(int left = 0; left < sArray.length; left++){
            for(int right = left; right < sArray.length; right++){
                if(isHuiWenChuan(sArray, left, right)){
                    if(max < right - left + 1){
                        max = right - left + 1;
                        resultLeft = left;
                        resultRight = right;
                    }
                }
            }
        } 

        return new String(sArray, resultLeft, resultRight - resultLeft + 1);
    }

    private boolean isHuiWenChuan(char[] sArray, int left, int right)
        {
            int i = left;
            int j = right;
            while(i < j){
                if(sArray[i] == sArray[j]){
                    i++;
                    j--;
                }else{
                    return false;
                }
            }
            return true;
        }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

注意:
char[] sArray = s.toCharArray();是char[] 不是int[]
字符串与数组的转换:
字符串转数组
数组转字符串

思路2:DP

DP三大步骤:

是否为最值问题?

最长回文子串,是

能否使用DP解决?
  1. 原问题能否分解为子问题?
  2. 无后效性
DP解题步骤
  1. 确定dp含义
    dp[i][j]表示字符串s从i到j是不是回文子串
    注意,dp[i][j]存储的是和否

  2. 确定边界值
    dp[0][0]、dp[1][1]、dp[2][2]、…、dp[k][k] = true;

  3. 确定状态转移方程
    假如现在已知dp[i - 1][j]的结果,能推出dp[i][j]的结果吗?
    假如现在已知dp[i][j - 1]的结果,能推出dp[i][j]的结果吗?

如果有字符串aba,已知ab不是回文,那么aba是回文
如果有字符串abc,已知ab不是回文,那么abc不是回文

如果有字符串aaa,已知aa是回文,那么aaa是回文
如果有字符串aab,已知aa是回文,那么aab不是回文

并不能通过前面的dp[i - 1][j]或dp[i][j - 1]的结果推出dp[i][j]的结果,那,

怎么办呢?

在这里插入图片描述
分两种情况考虑:

  1. 如果s[i, j]的长度:j - i + 1 <= 2
    <=2那么,只有0,1,2
    0的情况已经在开始排除
    1的情况也已经考虑过dp[i, i] = true;
    那么,剩下的只有长度为2
    如果j - i + 1 = 2
    那么dp[i, j] = s[i] == s[j];并且,长度为1的时候,也可以使用这个公式

  2. 如果s[i, j]的长度:j - i + 1 > 2
    如果s[i + 1, j - 1]是回文子串,且s[i] == s[j],那么s[i, j]是回文子串
    dp[i, j] = dp[i + 1, j - 1] && s[i] == s[j];

左下角的值,推到出右上角的值

class Solution {
    public String longestPalindrome(String s) {
        //边界值
        if(s == null) return null;
        if(s.length() == 0) return  "";

        //字符转数组
        char[] sArray = s.toCharArray();

        //建立dp,里面存储的是true,false
        boolean [][] dp = new boolean[sArray.length][sArray.length];
        
        int left = 0;
        int maxlength = 1;
        //先求左下,再求右上,这就要求i需要从大到小,j从小到大
        for(int i = sArray.length - 1; i>=0; i--){
            for(int j = i; j<sArray.length; j++){
                if(j - i + 1 <= 2){
                    dp[i][j] = (sArray[i] == sArray[j]);
                }else{
                    dp[i][j] = dp[i + 1][j - 1] && (sArray[i] == sArray[j]);
                }

                if(dp[i][j] && (maxlength < j - i + 1)){
                    maxlength = j - i + 1;
                    left = i;
                }
            }
        }

        return new String(sArray, left, maxlength);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

注意:
dp[i, j] = dp[i + 1, j - 1] && s[i] == s[j];
先求左下,再求右上
这就要求i需要从大到小,j从小到大

思路3:扩展中心法

以某一个点为中心,左右扩展,左右相等,则是回文子串(下图左)
但,这些子串都是奇数个,回文子串的偶数个没有被计算,因此,需要借助元素之间的间隙,左右扩展,从而找出偶数类型的回文子串(下图右)
在这里插入图片描述
假设字符串“abbaba”的长度为n,那么,可以作为扩展中心的个数为:n + (n - 1) = 2*n - 1

时间复杂度为O((2n - 1) * n) = O(2n^2 - n) = O(n ^2)
空间复杂度为O(1)

class Solution {
    public String longestPalindrome(String s) {
        //边界值
        if(s == null) return null;
        if(s.length() == 0) return s;

        char[] sArray = s.toCharArray();
        ArrayList list = new ArrayList();

        int max = 1;
        int begin = 0;
        for(int i = 0; i < sArray.length; i++){
            int left = i;
            int right = i;
            int count = 1;
            while(--left >= 0 && ++right < sArray.length){
                if(sArray[left] == sArray[right]){
                    count = count + 2;
                    if(max < count){
                        max = count;
                        begin = left; 
                    }
                }else{
                    break;
                }
            }
        }

        for(int i = 0; i < sArray.length - 1; i++){
            int left = i;
            int right = i + 1;
            int count = 0;
            while(left >= 0 && right < sArray.length){
                if(sArray[left] == sArray[right]){
                    count = count + 2;
                    if(max < count){
                        max = count;
                        begin = left; 
                    }
                }else{
                    break;
                }
                left--;
                right++;
            }
        }
        return new String(sArray, begin, max);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

思路4:马拉车法

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/799596
推荐阅读
相关标签
  

闽ICP备14008679号