leetcode:44. 通配符匹配
题目
给定一个字符串 (s
) 和一个字符模式 (p
) ,实现一个支持 '?'
和 '*'
的通配符匹配。
'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。
说明:
s
可能为空,且只包含从a-z
的小写字母。p
可能为空,且只包含从a-z
的小写字母,以及字符?
和*
。
示例:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
输入:
s = "aa"
p = "*"
输出: true
解释: '*' 可以匹配任意字符串。
输入:
s = "cb"
p = "?a"
输出: false
解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。
输入:
s = "adceb"
p = "*a*b"
输出: true
解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce".
输入:
s = "acdcb"
p = "a*c?b"
输出: false
解答 & 代码
二维动态规划:
- 动态规划数组
**dp**
:dp[i][j]
代表字符串s
的前i
个字符s[0,...,i-1]
和字符模式p
的前j
个字符p[0,...,j-1]
是否匹配 - 状态转移方程:
- 如果
p[j - 1] = '*'
,则dp[i][j] = dp[i][j - 1] || dp[i - 1][j - 1] || dp[i - 1][j]
dp[i][j - 1]
代表'*'
匹配 0 个字符,因此消去'*'
即p[j-1]
dp[i - 1][j - 1]
代表'*'
匹配 1 个字符,因此消去s[i-1]
和p[j-1]
dp[i - 1][j]
代表'*'
匹配 n 个字符(n > 1),因此消去s[i-1]
,保留p[j-1]
的'*'
继续匹配
- 否则,
p[j - 1]
不是'*'
,dp[i][j] = dp[i - 1][j - 1] && match(s[i - 1], p[j - 1])
- 也就是看
s[i-1]
和p[j-1]
是否匹配,以及消去s[i-1]
和p[j-1]
后剩余部分是否匹配
- 也就是看
- 如果
初始化:
dp[0][0] = true
,s
和p
都为空串时匹配dp[0][j] = dp[0][j - 1] && p[j - 1] == '*'
,s
为空串,p
不为空,若想匹配,则p
只能全为'*'
dp[i][0] = false
,s
不为空串,而p
为空串,则不可能匹配class Solution { private: bool match(char a, char b) { return a == b || b == '?'; } public: bool isMatch(string s, string p) { int sLen = s.size(); int pLen = p.size(); // 动态规划数组 dp:dp[i][j] 代表字符串 s 的前 i 个字符 s[0,...,i-1] 和 // 字符模式 p 的前 j 个字符 p[0,...,j-1] 是否匹配 vector<vector<bool>> dp(sLen + 1, vector<bool>(pLen + 1)); // 初始化 dp[0][0] = true; for(int j = 1; j <= pLen; ++j) dp[0][j] = dp[0][j - 1] && p[j - 1] == '*'; for(int i = 1; i <= sLen; ++i) dp[i][0] = false; // 状态转移 for(int i = 1; i <= sLen; ++i) { for(int j = 1; j <= pLen; ++j) { if(p[j - 1] == '*') dp[i][j] = dp[i][j - 1] || // * 匹配 0 次 dp[i - 1][j - 1] || // * 匹配 1 次 dp[i - 1][j]; // * 匹配 n 次 else dp[i][j] = dp[i - 1][j - 1] && match(s[i - 1], p[j - 1]); } } return dp[sLen][pLen]; } };
复杂度分析:设字符串
s
长为 N,字符模式p
长为 M
时间复杂度 O(NM):
- 空间复杂度 O(NM):
执行结果:
执行结果:通过
执行用时:88 ms, 在所有 C++ 提交中击败了 23.55% 的用户
内存消耗:11 MB, 在所有 C++ 提交中击败了 68.60% 的用户