剑指Offer(19)正则表达式匹配

目录

正则表达式匹配

描述

示例 1

示例 2

示例 3

示例 4

示例 5

说明

方法一:递归

方法二:动态规划


正则表达式匹配

描述

请实现一个函数用来匹配包含 '. ' 和 '*' 的正则表达式。

模式中的字符 '.' 表示任意一个字符,而 '*' 表示它前面的字符可以出现任意次(含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 的小写字母以及字符 . 和 *,无连续的 '*'。

方法一:递归

我们可以直接使用递归的方法,先对第一个字母进行判断,两个字母如果匹配则进行判断:

  1. 判断正则表达式p的第二个字母是否为'*',为‘*’则需要考虑第一个字符是当作出现1+次还是0次
  2. 如果第二个字母不是'*',则s和p都右移一位,进行下一轮判断。
class Solution {
    public boolean isMatch(String s, String p) {
        if (p.isEmpty()){
            return s.isEmpty();
        }
        boolean firstMatch=!s.isEmpty() && (s.charAt(0)==p.charAt(0) || p.charAt(0)=='.');
        if (p.length() >=2 && p.charAt(1)=='*'){//如果*号前面有字符
            return (firstMatch && isMatch(s.substring(1),p) || isMatch(s,p.substring(2)));//*字符出现1+次或者不出现
        }else{
            return firstMatch && isMatch(s.substring(1),p.substring(1));//没有*字符就正常判断
        }
    }
}

这个方法思路非常简单,但是时间和空间消耗都非常大。

方法二:动态规划

 我们假设主串为A(长度为n),模式串为B(长度为m),从模式串B最后一个字符开始考虑,有三种情况:

  1. 若B的最后字符为正常字符,查看A[n-1]与B[m-1]是否相等,相等则查看A的前n-1个字符与B的前m-1个字符是否匹配,如果不相等则直接返回false
  2. 若B的最后字符为'.',那么它可以匹配任意字符,我们就理解为A[n-1]与B[m-1]相等,继续比较前面的字符
  3. 若B的最后字符为'*',则B[m-2]=charA可以重复0次或多次,此时有两种情况
    1. A[n-1]!=charA,那么就是重复0次,我们比较A的前n-1个字符和B的前m-3个字符是否匹配
    2. A[n-1]==charA,那就是重复多次,我们需要将A往前移,继续判断是否与charA相等,比较A的前n-2个字符和B的前m-1个字符是否匹配

思路清晰之后我们假设dp[i][j]A的前i个字符和B的前j个字符是否匹配,

  1. 前两种情况状态转移方程都可以表示为:dp[i][j]=dp[i-1][j-1]
  2. 第三种情况则分为重复0次和多次
    1. 重复0次:dp[i][j]=dp[i][j-2]
    2. 重复多次:dp[i][j]=dp[i-1][j]

我们还需要对初始条件进行赋值,

  • 空字符串与空正则表达式:匹配,所以dp[0][0]=true
  • 空字符串与非空正则表达式:需要计算
  • 非空字符串与空正则表达式:不匹配
  • 非空字符串与非空正则表达式:需要计算
class Solution {
    public boolean isMatch(String A, String B) {
        int n = A.length();
        int m = B.length();
        boolean[][] f = new boolean[n + 1][m + 1];

        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                //分成空正则和非空正则两种
                if (j == 0) {
                    f[i][j] = i == 0;
                } else {
                    //非空正则分为两种情况 * 和 非*
                    if (B.charAt(j - 1) != '*') {
                        if (i > 0 && (A.charAt(i - 1) == B.charAt(j - 1) || B.charAt(j - 1) == '.')) {
                            f[i][j] = f[i - 1][j - 1];
                        }
                    } else {
                        //碰到 * 了,分为0次和多次两种情况
                        //0次
                        if (j >= 2) {
                            f[i][j] |= f[i][j - 2];
                        }
                        //多次
                        if (i >= 1 && j >= 2 && (A.charAt(i - 1) == B.charAt(j - 2) || B.charAt(j - 2) == '.')) {
                            f[i][j] |= f[i - 1][j];
                        }
                    }
                }
            }
        }
        return f[n][m];
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_39478524/article/details/120551158