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

编辑距离

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

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

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

示例 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 由小写英文字母组成

思路

还是按照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];
    }
}

注意
两层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;
        }
}

注意:
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);
    }
}

注意:
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);
    }
}

思路4:马拉车法

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值