(每日一题)LeetCode10:正则表达式匹配

1、题目描述

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。

  • '.' 匹配任意单个字符
  • '*' 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

示例 1:

输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。

2、题解

分析过程:

        首先,我们需要明确动态规划的基本思想:将问题分解为子问题,并存储子问题的解以避免重复计算。在这个问题中,我们关注的是字符串str和模式串pattern的匹配问题。

①定义dp[i][j]为:当字符串str的前i个字符与模式串pattern的前j个字符是否匹配。

注意,dp[i][j]的下标对应字符串str的下标为i-1,对应模式串pattern的下标为j-1。

②初始化:dp[0][0] = true,表示空字符串与空模式匹配

③处理str为空的情况:若str为空,要使得匹配成功,则pettern的右端必为*,这样才能将前面的字符消去,也就是这种形式: ....a*,对应状态转移方程:dp[0][i] = dp[0][i - 2]

④状态转移方程:

        当前字符不为‘*’,若当前字符不匹配,则匹配失败;当前字符匹配成功的情况有两种分别为:str.charAt(i - 1) == pattern.charAt(j - 1)和pattern.charAt(j - 1) == '.',对应状态转移方程为:dp[i][j] = dp[i - 1][j - 1]。 

        当前字符为‘*’,需要考虑两种情况:str当前字符(对应i-1)与pattern前一个字符(对应j-2)是否匹配

        (i)若匹配,即str.charAt(i - 1) == pattern.charAt(j - 2) || pattern.charAt(j - 2) == '.'
   当前字符与前一个字符或'.'匹配,可以选择匹配0次、1次或多次,对应状态转移方程为:

        匹配0次:dp[i][j] = dp[i][j - 2]

        匹配1次:dp[i][j] = dp[i - 1][j - 2]

        匹配多次:dp[i][j] = dp[i - 1][j]

        这里解释一下匹配多次的情况,个人是这样理解的,可以参考:
        当pattern匹配N次,最右端的a与str当前字符a匹配(可以看作消去一个a),则还剩下N-1个a,仍然可以看作a*形式,所以转移后pattern下标不变,仍考虑前j个字符,即dp[i][j] = dp[i - 1][j]

        

        (ii)若不匹配,则需要*将前一个字符消去,对应状态转移方程:dp[i][j] = dp[i][j - 2]

通过以上分析,可以得到完整的动态规划状态转移方程。通过填充dp数组,最终可以确定字符串`str`是否与模式串`pattern`匹配。

使用动态规划求解,代码如下:

class Solution {  
    // isMatch方法,判断str是否与pattern匹配  
    public boolean isMatch(String str, String pattern) {  
        // 获取str和pattern的长度  
        int m = str.length();  
        int n = pattern.length();  
          
        // 创建一个二维布尔数组dp,用于存储中间结果  
        boolean[][] dp = new boolean[m + 1][n + 1];  
          
        // 初始化,空字符串与空模式串匹配  
        dp[0][0] = true;  
          
        // 处理str为空的情况
        for (int i = 1; i < n + 1; i++) { 
            if (pattern.charAt(i - 1) == '*') 
                dp[0][i] = dp[0][i - 2]; // 则不匹配,与前一个字符的状态相同  
        }  
          
        // 使用双重循环遍历str和pattern的所有字符  
        for (int i = 1; i < m + 1; i++) {  
            for (int j = 1; j < n + 1; j++) {
                // 当前字符不为'*',如果当前字符相等或者模式串的当前字符是'.'  
                if (str.charAt(i - 1) == pattern.charAt(j - 1) || pattern.charAt(j - 1) == '.') {  
                    dp[i][j] = dp[i - 1][j - 1];  
                } else if (pattern.charAt(j - 1) == '*') { 
                    // 前一个相等,可以复制0次、1次、>=2次,三种情况之一为真则dp[i][j]为真  
                    if (str.charAt(i - 1) == pattern.charAt(j - 2) || pattern.charAt(j - 2) == '.') {  
                        dp[i][j] = dp[i][j - 2] || dp[i - 1][j - 2] || dp[i - 1][j];  
                    } else // 前一个不相等,只考虑'*'匹配0次的情况  
                        dp[i][j] = dp[i][j - 2];  
                }  
            }  
        }  
        // 返回dp数组的最后一个元素,即str和pattern的匹配结果  
        return dp[m][n];          
    }  
}

如果仍然理解困难,可参考以下链接手画图解
. - 力扣(LeetCode)

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ai旅人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值