Leetcode【动态规划】| 正则表达式匹配

题目

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
  ‘.’ 匹配任意单个字符
  ‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
说明:
  s 可能为空,且只包含从 a-z 的小写字母。
  p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
示例 1:
 输入:
  s = “aa”
  p = “a”
 输出: false
 解释: “a” 无法匹配 “aa” 整个字符串。
示例 2:
 输入:
  s = “aa”
  p = “a*”
 输出: true
 解释: 因为 ‘*’ 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 ‘a’。因此,字符串 “aa” 可被视为 ‘a’ 重复了一次。
示例 3:
 输入:
  s = “ab”
  p = “.*” 
 输出: true
 解释: “.*” 表示可匹配零个或多个(’ * ’ )任意字符(’ . ')。
示例 4:
 输入:
  s = “aab”
  p = “c*a*b”
 输出: true
 解释: 因为 ‘*’ 表示零个或多个,这里 ‘c’ 为 0 个, ‘a’ 被重复一次。因此可以匹配字符串 “aab”。
示例 5:
 输入:
  s = “mississippi”
  p = “mis*is*p*.”
 输出: false
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/regular-expression-matching*

解题

题目解析

 该题目的意思是,s是一个只包含小写字母的字符串,p是一个既包含小写字母又含有两个正则符号(’ . ’ 和 ’ * ‘)的字符串。判断s和p是否能匹配,就是p中正则符号替换成对应字母后是否能和s完全相同或者包含与s完全相同的子字符串。(s和p可以为空)
 匹配的关键除了字母的相同,主要在于p中两个正则运算符的替换/匹配。
 ’ . ’ :匹配任意单个字符,表示p中出现的’ . ’ 可以被替换成任意一个小写字母。
 ’ * ’ :匹配零个或多个前面的那一个元素,表示p中出现的’ * ’ ,可以替换成0个或多个其前面的那个字符,比如a*,可以替换成a或aa或aaa或aaaaa…(n个a)。

思路

 采用动态规划的思路

  1. dp状态:dp[i][j] ,表示 s 中前 i 个字符和 p 中前 j 个字符是否能匹配(注意:这里表示的第i个字符之前和第j个字符之前,对应字符串的下标是i-1和j-1)
  2. 边界条件:dp[0][0] = true; 表示s和p前0个字符是匹配的,就是两个空字符串是匹配的。
  3. 状态转移方程:(从前往后递推,不同的状态转移方式主要取决于p中的正则符号的不同)分为两种情况:p中当前字符串为’ * ’ 时与当前字符串为字母时(因为如果当前为’ . ‘则可以匹配任意的字母所以肯定为true)
    ① 当p遍历到第j个字符时,p[j] = ’ * ’ ,要判断 ‘ * ’ 前面的字符p[j-1]与当前i是否匹配因为 ‘ * ’ 要考虑替换成它前一个字符是否能匹配。
    d p [ i ] [ j ] = { d p [ i − 1 ] d p [ j ]   o r   d p [ i ] [ j − 2 ] , s[i-1] == p[j-2] 即matches(s,i,p,j-1) d p [ i ] [ j − 2 ] , 其他 dp[i][j] = \begin{cases} dp[i-1]dp[j]\ or\ dp[i][j-2],\text{s[i-1] == p[j-2] 即matches(s,i,p,j-1)}\\ dp[i][j-2],\text{其他} \end{cases} dp[i][j]={dp[i1]dp[j] or dp[i][j2]s[i-1] == p[j-2] matches(s,i,p,j-1)dp[i][j2]其他
    ② 当p遍历到第j个字符时,p[j] 不是’ * '时 ,直接判断p中当前j和s中当前i是否相等即可转换为dp[i-1][j-1]:
    d p [ i ] [ j ] = { d p [ i − 1 ] d p [ j − 1 ] , s[i-1] == p[j-1]即matches(s,i,p,j) f a l s e , 其他即当前i和j字符不相等的情况 dp[i][j] = \begin{cases} dp[i-1]dp[j-1],\text{s[i-1] == p[j-1]即matches(s,i,p,j)}\\ false,\text{其他即当前i和j字符不相等的情况} \end{cases} dp[i][j]={dp[i1]dp[j1]s[i-1] == p[j-1]matches(s,i,p,j)false,其他即当前ij字符不相等的情况

具体见代码注释

java实现

class Solution {
    public boolean isMatch(String s, String p) {
        //动态规划
        int m = s.length();
        int n = p.length();

        boolean[][] dp = new boolean[m+1][n+1];
        dp[0][0] = true;
        for(int i = 0; i <= m; i++){
            for(int j = 1; j<=n;j++){
                if(p.charAt(j-1) == '*'){//p的第一个不可能为'*',所以可以有j-2 dp[j]对应的字符下标是j-1
                //如果p中第j个字符为*时,ij匹配可以时将当前*和其前面一个字符视为整体

                    //① p中第j个数为*,比较s中第i个数和p中第j-1个数,如果能匹配上说明j也可以和j-1一样和i匹配上,即s[i-1]==p[j-2]==p[j-1],那么dp[i][j] = dp[i-1][j-2];因为*表示可以匹配多个,所以不能单纯的转移到dp[i-1][j-2],可以继续比较第i-1个数和j-1个数,所以要转移到dp[i-1][j]
                    //② 因为*可以匹配零个元素前面的元素,也就是可以把*和前面的字符消掉,dp[i][j] = dp[i][j-2];
                    //以上两种情况其中有一个true就可以,所以用||
                     //②
                    if(matches(s,p,i,j-1)){
                        dp[i][j] = dp[i-1][j];
                    }
                    dp[i][j] = dp[i][j] || dp[i][j-2];
                }
                //p中第j个字符不为*
                else{
                    //s中第i个和p中第j个字符相等
                    if(matches(s,p,i,j)){
                        dp[i][j] = dp[i-1][j-1];
                    }
                }
            }
        }
        return dp[m][n];
    }

    //s中第i个字符和p中第j个字符是否能匹配所以要比s[i-1]和j[i-1]
    public boolean matches(String s, String p, int i, int j){
        if(i == 0){
            return false;
        }
        if(p.charAt(j-1) == '.'){
            return true;
        }
        return s.charAt(i-1) == p.charAt(j-1);
    }
}

在这里插入图片描述
参考:力扣官方题解

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值