刷题日记:正则表达式的匹配

本文介绍了一个用于匹配包含'.'和'*'的正则表达式的函数实现。通过动态规划的方法,详细解析了如何根据输入字符串与模式字符串之间的关系,判断二者是否匹配。包括特殊字符'*'的两种使用情况及其状态转移过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、题目

        

请实现一个函数用来匹配包含'. '和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但与"aa.a"和"ab*a"均不匹配。

示例 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
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母以及字符 . 和 *,无连续的 '*'。

题目来源:https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof

2、思路

        我们先假设两个字符串的长度,设:

                s.length() = n

                p.length() = m;

        再设s的字符下标为i,p的字符下标为j,我们用 f [s : i, p : j] 来表示当s字符串以下标为 i 的字

        符为结尾时,和p字符串以下标为 j 的字符为结尾时,二者所代表的字符串是否等价,是一个

        布尔值

        根据题目要求,最终需要得到的结果是:当i = n - 1, j = m - 1时, f [s : i,  p : j]能够匹配,

        而f [s : i,  p : j]的状态(是否匹配)是可以从前面的状态转移而来的

我们将 f [s : i,  p : j] 表示成一个boolean类型的二维数组

 boolean[][] dp = new boolean[n + 1][m + 1]

dp[0][0]表示当两个字符串都为空串时能否匹配,根据题意可知 dp[0][0] = true

dp[i][j]表示f [s : i - 1, p : j - 1]的状态,true为匹配,false为不匹配

判断dp[i][j]的条件如下:

        当p字符串以下标为j - 1的字符为结尾时,若该字符为 '*',有以下规律

                1、当 * 的作用是使下标为 j - 2的字符重复0次时

                       这可以看成是将下标为 j - 2的字符消除,这时只需要判断以下标为 j - 3 为结尾的字

                       符串与以下标为  i - 1为结尾的能否匹配,即dp[i][j - 2]的状态

                        也即:当dp[i][j - 2] = true时, dp[]i[j] = true

                2、当 * 的作用是使下标为 j - 2 的字符重复时

                     首先自然是要先判断 * 之前的字符也即下标为j - 2下标与为 i - 1的字符是否等价,等

                      价的情况有两种,第一种是 * 之前的字符与下标为 i - 1的字符相同,第二种是 * 之

                      前的字符为 . ,若此步判断结果为true,则进行以下判断,反之:dp[i][j] = false

                    判断dp[i - 1][j]的状态是否为true,原因如下:

                    (1)若dp[i - 1][j] = true时,* 在上一个字符的匹配时起的是消除作用,可看例子:

                                s字符串:aa

                                p字符串:aab*

                        此时消除掉b字符刚好能够使得二者匹配,此时加入下标为 i - 1的字符:b

                        就变成了:

                                s字符串:aab

                                p字符串:aab*

                       依旧可以匹配,此时 * 的作用由消除 b 转变成了使 b 重复一次

                   (2)dp[i][j] = true时, * 在上一个字符匹配时起的是重复作用,有例子如下:

                                s字符串:aaaa

                                p字符串:aaa*

                        此时加入下标为 i - 1的字符:a

                                s字符串:aaaaa

                                p字符串:aaa*

                        依旧可以匹配,此时 * 的作用是由重复一次变成了重复两次 

        当p字符串以下标为j - 1的字符为结尾时,若该字符不为 '*',则

                判断该字符与下标为 i - 1的字符是否等价,等价的条件为相同或该字符为 .,若等价则

                进行后续判断,反之则:dp[i][j] = false

               接着判断以下标为i - 1为结尾的字符串和以下标为 j - 2的字符为结尾的字符串是否匹配

                也即:dp[i - 1][j - 1]的状态即可,若为true则dp[i][j] = true,反之则dp[i][j] = false

3、编码

                 

class Solution {
    public boolean isMatch(String s, String p) {
        char[] ss = s.toCharArray();
        char[] ps = p.toCharArray();
        int n = ss.length + 1, m = ps.length + 1;
        boolean[][] dp = new boolean[n][m];
        dp[0][0] = true;
        //判断s为空串时能否匹配
        for(int j = 2; j < m; j += 2) {
            dp[0][j] = dp[0][j - 2] && ps[j - 1] == '*';
        }
        for(int i = 1; i < n; i++) {
            for(int j = 1; j < m; j++) {
                if(ps[j - 1] == '*') {
                    if(j >= 2) {
                        dp[i][j] = dp[i][j - 2];
                    }
                    dp[i][j] = dp[i][j] || (ss[i - 1] == ps[j - 2] || ps[j - 2] == '.') && dp[i - 1][j];
                }else {
                    dp[i][j] = (ss[i - 1] == ps[j - 1] || ps[j - 1] == '.') && dp[i - 1][j - 1];
                }
            }
        }
        return dp[n - 1][m - 1];
    }
}

                                

                        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值