Leetcode - 通配符匹配

156 阅读1分钟

这是我参与更文挑战的第4天,活动详情查看更文挑战

题目描述

Given an input string (s) and a pattern (p), implement wildcard pattern matching with support for '?' and '*' where:

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

  • '?' Matches any single character.'?' 可以匹配任何单个字符。
  • '*' Matches any sequence of characters (including the empty sequence).'*' 可以匹配任意字符串(包括空字符串)

The matching should cover the entire input string (not partial).

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

Example 1:

Input: s = "aa", p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".

Example 2:

Input: s = "aa", p = "*"
Output: true
Explanation: '*' matches any sequence.

Example 3:

Input: s = "cb", p = "?a"
Output: false
Explanation: '?' matches 'c', but the second letter is 'a', which does not match 'b'.

Example 4:

Input: s = "adceb", p = "*a*b"
Output: true
Explanation: The first '*' matches the empty sequence, while the second '*' matches the substring "dce".

Example 5:

Input: s = "acdcb", p = "a*c?b"
Output: false

Constraints:

  • 0 <= s.length, p.length <= 2000
  • s contains only lowercase English letters.
  • p contains only lowercase English letters, '?' or '*'.

解题思路

比较经典的动态规划解题

根据题目,'?'和「小写字母」的匹配模式是确定的,不确定的因素是'*'的匹配,可为空或者任意数量的任意字符。

使用dp[i][j]dp[i][j]表示字符串s的的前 i 个字符和模式 p 的前jj个字符是否能匹配。在进行状态转移时,可以考虑模式 p 的第 jj 个字符 pjp_j,与之对应的是字符串 s 中的第 ii 个字符 sis_i

如果 pjp_j 是小写字母,那么 sis_i 必须也为相同的小写字母,状态转移方程为

dp[i][j]=(sipj相同)dp[i1][j1]dp[i][j]=(s_i与p_j相同) \land dp[i-1][j-1]

如果pjp_j 是问号,那么对 sis_i 没有任何要求,状态转移方程为

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

如果 pjp_j 是星号,那么同样对 sis_i 没有任何要求,但是星号可以匹配零或任意多个小写字母,因此状态转移方程分为两种情况,即使用或不使用这个星号

dp[i][j]=dp[i][j1]dp[i1][j]dp[i][j]=dp[i][j-1]\lor dp[i-1][j]

接下来需要确定边界情况 dp[0][0]=Truedp[0][0]=True,即当字符串 s 和模式 p 均为空时,匹配成功;

dp[i][0]=Falsedp[i][0]=False,即空模式无法匹配非空字符串;

dp[0][j]dp[0][j]需要分情况讨论:因为星号才能匹配空字符串,所以只有当模式 p 的前 jj 个字符均为星号时,dp[0][j]dp[0][j] 才为真。

代码

Python代码

class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        m, n = len(s), len(p)

        dp = [[False] * (n + 1) for _ in range(m + 1)]
        dp[0][0] = True
        for i in range(1, n + 1):
            if p[i - 1] == '*':
                dp[0][i] = True
            else:
                break
        
        for i in range(1, m + 1):
            for j in range(1, n + 1):
                if p[j - 1] == '*':
                    dp[i][j] = dp[i][j - 1] | 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]