题解 | #正则表达式匹配#

正则表达式匹配

http://www.nowcoder.com/practice/28970c15befb4ff3a264189087b99ad4

描述

题目描述

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

示例
输入:"aaa","a*a"
返回值:true
引言

知识点:动态规划,递归,正则表达式
难度:⭐⭐⭐⭐


题解

解题思路

对于动态规划解法,总的思路便是状态转移,即不断判断从 str[:1] 和 pattern[:1] ,即从第一个字符开始判断是否匹配,直到整个字符串完全匹配。

如下图的图解,每次状态转移总共有两种状态:(1)str 增加一个字符,判断是否匹配(2)pattern增加一个字符,判断是否匹配

方法一:动态规划

  • 每个字符有三种情况出现:“字符”、”.“、“*”
  • 状态定义:dp[i][j]表示字符串 str的前 i 个字符串和 pattern 的前 j 个字符串是否匹配
  • 状态转移:主要是对 “*” 的状态处理
    • pattern[i - 1] != “*”,即上一位不为*。dp[i][j]在以下任何一种情况为true时,则等于true
      • 1、dp[i - 1][j - 1] && str[i - 1] == pattern[j - 1],即上一个状态和当前位的字符都匹配
      • 2、dp[i - 1][j - 1] && pattern[j - 1] == '.',即上一个状态为true,且pattern上一位包含 ‘.’
    • pattern[i - 1] == “*”,即当前位的上一位为*。dp[i][j]在以下任何一种情况为true时,则等于true
      • 1、dp[i][j - 2],即 j-2 位 的pattern能满足,则 i-1 位是 ‘*’ 作为任意数量的值必定能满足匹配
      • 2、dp[i - 1][j] && str[i - 1] == pattern[j - 2];即让字符 pattern[j-2]多出现几次,看能否匹配
      • 3、dp[i - 1][j] && pattern[j - 2] == '.', 即让字符 '.' 多出现 1 次时,能否匹配;
  • 状态初始化
    • dp[0][0] == true 代表两个空字符串能够匹配。
    • dp[0][j] = dp[0][j - 2]p[j - 1] = '*', 即当前pattern的0到j位是true还是false,取决于dp[0][j-2]是否匹配,以及 pattern的当前位的上一位是否为‘*’,因为‘*’可以匹配任何值,包括空值
  • 返回值
    • dp 矩阵右下角字符,代表字符串 strpattern 能否匹配。
图解

image-20210703003601775

image-20210703010414270

算法流程:
  • 定义状态,并初始化
  • 遍历每个str和pattern中的字符
    • 1、当比较的位pattern[j-1]=='.', 或者字符相等匹配,则状态取决于上一次状态
    • 2、对于包含 * 的匹配
      • 当之前位为 '.', 或者字符相等,则匹配
      • 否则只能不匹配
  • 返回状态转移最后的结果
Java 版本代码如下:
import java.util.*;
public class Solution {
    public boolean match (String str, String pattern) {
        int strLen = str.length() + 1, patternLen = pattern.length() + 1;
        // 定义状态,并初始化
        boolean[][] dp = new boolean[strLen][patternLen];
        dp[0][0] = true;
        // 初始化首行
        for(int j = 2; j < patternLen; j++) {
            // 因为pattern[j-1]的 * 可以取任意值包括空值,因此dp[0][j]相当于取决于dp[0][j-2]
            if(pattern.charAt(j - 1) == '*') {
                dp[0][j] = dp[0][j - 2];
            }
        }
        // 状态转移
        for(int i = 1; i < strLen; i++) {
            for(int j = 1; j < patternLen; j++) {
                // 当比较的位pattern[j-1]=='.', 或者字符相等匹配,则状态取决于上一次状态
                if (pattern.charAt(j - 1) == '.' || str.charAt(i - 1) == pattern.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];   
                } else if (j >= 2 && pattern.charAt(j - 1) == '*'){
                    // 对于包含 * 的匹配
                    // 当之前位为 '.', 或者字符相等,则匹配
                    if (pattern.charAt(j - 2) == '.' || pattern.charAt(j - 2) == str.charAt(i - 1)) {
                        dp[i][j] = dp[i][j - 2] || dp[i - 1][j - 2] || dp[i - 1][j];
                    } else {
                        // 否则只能不匹配
                        dp[i][j] = dp[i][j - 2];
                    }
                }
            }
        }
        return dp[strLen - 1][patternLen - 1];
    }
}
Python 版本代码如下:
class Solution:
    def match(self, s, p):
        if not s and not p:
            return True
        m, n = len(s) + 1, len(p) + 1  # 两个字符串的长度
        dp = [[False] * n for i in range(m)]
        # 初始化
        dp[0][0] = True
        for j in range(2, n, 2):
            dp[0][j] = dp[0][j - 2] and p[j - 1] == '*'
        # 状态转移
        for i in range(1, m):
            for j in range(1, n):
                if p[j - 1] == '*':
                    if dp[i][j - 2]: dp[i][j] = True
                    elif dp[i - 1][j] and s[i - 1] == p[j - 2]: dp[i][j] = True
                    elif dp[i - 1][j] and p[j - 2] == '.': dp[i][j] = True
                else:
                    if dp[i - 1][j - 1] and s[i - 1] == p[j - 1]: dp[i][j] = True
                    elif dp[i - 1][j - 1] and p[j - 1] == '.': dp[i][j] = True
        return dp[-1][-1]
复杂度分析:

时间复杂度 O(SP):其中 S,P 分别为 strpattern 的长度,状态转移需遍历整个 dp 矩阵
空间复杂度 O(SP):状态二维矩阵 dp

全部评论
方法描述里面的当 pattern [i-1] !="*" 后面方括号里应该是[j-1]吧?
点赞 回复 分享
发布于 2021-10-28 16:26

相关推荐

Twilight_m...:表格简历有点难绷。说说个人看法: 1.个人基本情况里好多无意义信息,什么婚姻状况、健康状况、兴趣爱好、户口所在地、身份证号码、邮政编码,不知道的以为你填什么申请表呢。 2.校内实践个人认为对找工作几乎没帮助,建议换成和测开有关的项目,实在没得写留着也行。 3.工作经历完全看不出来是干什么的,起码看着和计算机没啥关系,建议加强描述,写点你在工作期间的实际产出、解决了什么问题。 4.个人简述大而空,看着像AI生成,感觉问题最大。“Python,C,C++成为我打造高效稳定服务的得力工具”、“我渴望凭借自身技术知识与创新能力,推动人工智能技术的应用发展,助力社会实现智能化转型”有种小学作文的美感。而且你确定你个人简述里写的你都会嘛?你AI这块写的什么“深入研究”,发几篇顶会的硕博生都不一定敢这么写。而且你AI这块的能力和软测也完全无关啊。个人简述建议写你对哪些技术栈、哪些语言、哪些生产工具的掌握,写的有条理些,而且最好是和测开强相关的。
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
07-11 15:08
点赞 评论 收藏
分享
评论
4
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务