通配符匹配你了解吗

简介: 分析通配符的实现原理

通配符匹配

问题描述

44. 通配符匹配

给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '*' 的通配符匹配。

'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。

两个字符串完全匹配才算匹配成功。

说明:

  • s 可能为空,且只包含从 a-z 的小写字母。
  • p 可能为空,且只包含从 a-z 的小写字母,以及字符 ?*

示例:

输入: s = "adceb" p = "*a*b"

输出: true

解释: 第一个 ‘*’ 可以匹配空字符串, 第二个 ‘*’ 可以匹配字符串 “dce”。

分析问题

根据题意,给定的模式串p中,只有三种类型的字符出现,即小写字母a~z,问号 ‘?’ 和星号 ‘*’,其中 ‘小写字母’ 和 ‘问号’ 的匹配是确定的,即小写字母a~z只能匹配与之对应的小写字母,问号 ‘?’ 可以匹配任意一个小写字母;而星号的匹配是不确定的,它可以匹配零到任意多个小写字母,因此需要枚举所有可能匹配的情况。为了减少重复情况,我们可以使用动态规划来求解。

我们用dp[i] [j] 表示字符串s的前i个字符和模式串p的前j个字符是否能匹配。下面我们来看一下如何求状态转移方程。

对于模式串 p 的第 j 个字符 pj 和 字符串s的第 i 个字符 si 的匹配过程如下。

  1. 如果 pj 是小写字母,要想dp[i] [j]为真,那么 si 必须和 pj 相同,否则为假。即状态转移方程为

    dp[i] [j] = (si==pj ) and dp[i-1] [j-1]

​ 即要想dp[i] [j]为真,当且仅当 si==pj 为True,并且dp[i-1] [j-1] 也为True。

  1. 如果 pj 是问号‘?’,那么对 si 没有任何要求,即状态转移方程为:

    dp[i] [j] = dp[i-1] [j-1]

  2. 如果 pj 是星号‘* ’,那么对 si 没有任何要求,但是星号可以匹配零到任意多个小写字母,因此状态转移方程分为两种情况。

    • 如果星号表示多个字符,我们把该星号留着,即j不动,i-1,如果dp[i-1] [j]能匹配,则dp[i] [j] 也能匹配,因为星号 ‘*’ 可以表示任意字符,所以当前i-1加上一个字符后的i也就能匹配。所以dp[i] [j] = dp[i-1] [j]
    • 如果星号表示的是空字符,则保持i不动,j-1,如果dp[i] [j-1]能匹配,则dp[i] [j]也能匹配,即dp[i] [j] = dp[i] [j-1]

综上所述,最终的状态转移方程如下所示:

​ dp[i] [j] =$$\begin{cases} dp[i-1] [j-1] , 当si == pj 或者pj 是问号时 \\ dp[i-1] [j] 或者 dp[i] [j-1],当pj时星号时\\False,其它情况 \end{cases}$$

下面我们来看一下边界条件。

  • dp[0] [0]=True,即当字符串s和模式串p都为空时,表示匹配成功。
  • dp[i] [0] = False,即当模式串p为空时,它是不能匹配任意非空字符串的,所以为False。
  • dp[0] [j] 是需要分情况来看的,因为只有星号‘’才能匹配空字符串,所以只有当模式串前j个字符都是星号‘ ’时,dp[0] [j] 才为真。

下面我们来看一个具体的例子,这里先说明一点,因为字符串的下标从 0 开始,所以下图中si 和 pj 分别对应着 s[i-1]和 p[j-1]。

对于字符串s = "adceb",p = "*a*b" 来说。首先,我们先初始化边界条件,如下图所示。

image-20211121120455893

image-20211121123400296

image-20211121123417587

image-20211121123436358

image-20211121123454688

下面我们来看一下代码的实现。

class Solution:
    def isMatch(self, s, p):
        #求出字符串的长度
        m, n = len(s), len(p)
        #创建状态转移矩阵
        dp = [[False] * (n + 1) for _ in range(m + 1)]
        #初始化边界条件
        dp[0][0] = True
        #如果pj的前j个字符都是'*',表示可以匹配空
        #字符串s,所以dp[0][j]=true
        for j in range(1, n + 1):
            if p[j - 1] == '*':
                dp[0][j] = True
            else:
                break

        for i in range(1, m + 1):
            for j in range(1, n + 1):
                #如果pj=='*',那么dp[i][j]=dp[i][j - 1] or dp[i - 1][j]
                if p[j - 1] == '*':
                    dp[i][j] = dp[i][j - 1] or dp[i - 1][j]
                # 如果pj=='?'或者si==pj
                # 那么dp[i][j]=dp[i][j - 1] or dp[i - 1][j]
                elif p[j - 1] == '?' or s[i - 1] == p[j - 1]:
                    dp[i][j] = dp[i - 1][j - 1]

        return dp[m][n]

该算法的时间复杂度是O(m n),空间复杂度也是O(m n)。其中 m 和 n 分别是字符串 s 和模式串 p 的长度。

相关文章
|
设计模式 缓存 自然语言处理
DDD领域驱动设计如何进行工程化落地
DDD领域驱动设计到底如何进行实际的工程化落地,为什么要进行领域分层?本文主要围绕DDD领域分层,设计了可落地的工程结构。
DDD领域驱动设计如何进行工程化落地
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?
424 5
|
人工智能 自然语言处理 安全
亲测有效:claude入口_claude官网入口_国内使用claude的两种方法
Claude,作为 Anthropic 公司潜心研发的杰作 ✨,凭借其卓越的自然语言处理能力、深刻的上下文理解和无懈可击的安全性 🛡️,在人工智能领域熠熠生辉。然而,由于一些客观因素的限
|
数据安全/隐私保护 Python Windows
三种方法,Python轻松提取PDF中全部图片
三种方法,Python轻松提取PDF中全部图片
525 3
阿里云公网IP地址多少钱一个?
阿里云公网IP价格因地域而异,如华北1(青岛)包年包月约20.70元/月,华北2(北京)及其他地区23元/月,香港30元/月,新加坡23元/月。按量付费模式下,保有费0.020元/小时,流量额外计费。
4017 0
阿里云公网IP地址多少钱一个?
|
监控 算法 Serverless
OpenCV这么简单为啥不学——1.12、使用ssim函数对两张照片进行相似度分析
OpenCV这么简单为啥不学——1.12、使用ssim函数对两张照片进行相似度分析
399 0
|
XML 存储 JSON
PyMuPDF 1.24.4 中文文档(二)(1)
PyMuPDF 1.24.4 中文文档(二)
659 0
|
存储 缓存 算法
[译] OpenSSL 3.0.0 设计
本文翻译 OpenSSL 官网文档:https://www.openssl.org/docs/OpenSSL300Design.htmlTongsuo-8.4.0 是基于 OpenSSL-3.0.3 开发,所以本文对 Tongsuo 开发者同样适用,内容丰富,值得一读!介绍本文概述了 OpenSSL 3.0 的设计,这是在 1.1.1 版本之后的 OpenSSL 的下一个版本。假设读者熟悉名为 &
299 0
[译] OpenSSL 3.0.0 设计
|
IDE 编译器 Linux
你应该搞懂的 C 语言头文件路径问题
聊聊系统路径位置,绝对路径与相对路径,正斜杠 `/` 与 反斜杠 `\` 使用说明 ...... by 矜辰所致
646 0
你应该搞懂的 C 语言头文件路径问题