关于两个字符串的匹配转化问题(左神算法课程笔记)

最近遇到好几个这种类型的问题,主要就是给你两个字符串,然后进行字符串自己的匹配或者转化,这类问题就是采用动态规划,二维的和一维的,这边选择两个经典的问题,来对这一类问题做一个总结。PS:楼主刚刚毕业不久,最近将自己去年的实习和秋招的备考资料整理了一下,其中,个人比较推荐的资料是“LeetCode 刷题班”、“玩转算法之leetcode分门别类”、“通关算法40讲”、“左神的BAT算法”、“左神的高频精讲”、“左神的基础和进阶算法”、“剑指java面试”、“Java 校招面试 google面试官亲授”,“牛客网叶神的项目”!需要资料的同学,可以加扣3196254974。

题目一[leetcode72]https://leetcode.com/problems/edit-distance/

给了两个字符串word1和word2,使用删除,添加,替换操作从word1转化到word2,每个操作代价为1,需要找到最小代价的转化方法。

算法原理&算法步骤

方法一:二维dp

使用一个二维的数组dp[][],其中dp[i+1][j+1]表示word1[0,i]转化到word2[0,j]需要的最小代价,那么现在来求解动态规划方程

考虑现在word1[i]和Word2[j],
如果word1[i]=word2[j],那么dp[i+1][j+1]=dp[i][j]
否则word1[i]!=word2[j]时,有这么几种情况
1. abcd abce 替换 dp[i+1][j+1]=dp[i][j]
2.abc abcd 添加 dp[i+1][j+1]=dp[i+1][j]
3.adcd abd 删除 dp[i+1][j+1]=dp[i][j+1]

有了上述动态规划的方程,接下来的问题就简单多了
需要特别注意的是dp[][]数组的第一行和第一列的初始化
第一行表示word1取了"",那么dp[0][j+1]=j+1(添加操作)
第一列表示word2去了"",那么dp[i+1][0]=i+1(删除操作)

代码如下:

public class Solution {
    public int minDistance(String word1, String word2) {
        int len1=word1.length();
        int len2=word2.length();
        int[][] dp=new int[len1+1][len2+1];//dp[i+1][j+1]表示word1[0,i],word2[0,j]的cnovert需要进行的编辑次数
        
        for(int j=0;j<len2;j++){
            dp[0][j+1]=j+1;
        }
        for(int i=0;i<len1;i++){
            dp[i+1][0]=i+1;
        }
        for(int i=0;i<len1;i++){
            for(int j=0;j<len2;j++){
                if(word1.charAt(i)==word2.charAt(j)){
                    dp[i+1][j+1]=dp[i][j];
                }
                else{
                    dp[i+1][j+1]=Math.min(dp[i][j],Math.min(dp[i][j+1],dp[i+1][j]))+1;
                                         //abcc abcd        abcd abc    abc abcd
                }
            }
        }
        return dp[len1][len2];
    }
}

方法二:一维dp

现在回过头来看看二维动态规划方程,

dp[i+1][j+1]=dp[i][j],word1[i]=word2[j]
dp[i+1][j+1]=min(dp[i][j],dp[i+1][j],dp[i][j+1])

dp[i+1][j+1]只与dp[i][j]、dp[i+1][j]、dp[i][j+1]有关,由于计算顺序也是一行一行的来,所以可以考虑进行复用,只使用一个一维的数组dp,但是假设在计算j-1的时候就更新了dp[j ],那么 上一行的dp[j]就不见了,在计算dp[j+1]的时候是需要上一行的dp[j]的,所以这里之前先不更新,在计算了dp[j+1]之后再去更新dp[j],并且用一个变量prev记忆dp[j],这样

dp[j+1]=dp[j],word1[i]=word2[j]
dp[j+1]=min(dp[j],dp[j+1],prev)  word1[i]!=word2[j],
其中dp[j+1]代表当前行j位置,dp[j]代表上一行j-1位置,prev代表当前行j-1位置

也是需要注意的是,word1取""的时候,也就是dp最开始的初始化
还有word2取"",也就是求解每一行的时候开始的时候的prev的初始化。
另外一点,由于dp[j+1]是在下一列才更新,所以最后一列在循环中没有得到更新,在一行计算完成后,需要给dp[word2.length]=prev单独赋值

public class Solution {
    public int minDistance(String word1, String word2) {
        int len1=word1.length();
        int len2=word2.length();
        int[] dp=new int[len2+1];
        for(int j=0;j<len2;j++){
            dp[j+1]=j+1;
        }
        int prev;
        int cur;
        for(int i=0;i<len1;i++){
            prev=i+1;
            for(int j=0;j<len2;j++){
                if(word1.charAt(i)==word2.charAt(j))
                    cur=dp[j];
                else{
                    cur=Math.min(dp[j+1],Math.min(dp[j],prev))+1;
                }
                dp[j]=prev;
                prev=cur;
            }
            dp[len2]=prev;
        }
        return dp[len2];
    }
}

题目二

[leetcode10]https://leetcode.com/problems/regular-expression-matching/
表达式匹配,给了字符串s和p,需要对s和p进行匹配,其中p的"."表示任意一个字符,p中的"*",表示0个或多个前边的字符

算法原理&算法步骤

同样是使用一个二维动态规划数组dp[][],其中dp[i+1][j+1]表示s[0,i],p[0,j]时候能够匹配,那么现在来求解动态规划方程

假如s[i]==p[j]||p[j]=='.'  那么 dp[i+1][j+1]=dp[i][j]
否则,如果p[j]=='*',如果s[i]!=p[j-1]&&p[j-1]!='.',直接抛弃a*,dp[i+1][j+1]=dp[i+1][j-1]
      否则,表明当前位置p s不同,前一个位置可以匹配,那么有以下几种可能
            abc abc* 直接抛弃* dp[i+1][j+1]=dp[i+1][j]
            abcd abc* *代表一个或多个字符 dp[i+1][j+1]=dp[i][j+1]
            abcccc abc* *把前边一个也拿走 dp[i+1][j+1]=dp[i+1][j-1]     

至此,动态规划方程已经得到
同样第一行的初始化,代表s为""p只有为aA这样的才可以匹配。
而第一列的话,如果p为空的话是一定不能匹配的,所以默认为false.

代码如下:

public class Solution {
    public boolean isMatch(String s, String p) {
        if(s==null||p==null)
            return false;
        boolean[][] dp=new boolean[s.length()+1][p.length()+1];//dp[i][j]表示以i-1为结尾位置的s子串和以j-1为结尾位置的p子串是否能够match
        dp[0][0]=true;
        for(int j=0;j<p.length();j++){//s取出子串为“”,如果p  1*2*3*这样子类型就能够匹配
            if(p.charAt(j)=='*'&&dp[0][j-1])
                dp[0][j+1]=true;
        }
        for(int i=0;i<s.length();i++){
            for(int j=0;j<p.length();j++){
                if(p.charAt(j)=='.'||p.charAt(j)==s.charAt(i)){
                    dp[i+1][j+1]=dp[i][j];    
                }
                else if(p.charAt(j)=='*'){
                    if(p.charAt(j-1)!='.'&&p.charAt(j-1)!=s.charAt(i)){//直接丢弃前边一个字符  a*之间被丢弃
                        dp[i+1][j+1]=dp[i+1][j-1];
                    }
                    else{
                    //aaa a*
                        dp[i+1][j+1]=dp[i+1][j]||dp[i+1][j-1]||dp[i][j+1];//abc abc* abcc* 
                        //a*          当做a        当做“”        当做多个a(注意这里不能是dp[i][j]这样的话就只是当做了一个a)
                    }
                }
            }
        }
        return dp[s.length()][p.length()];
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值