这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战
前言
力扣第四十四题 通配符匹配
如下所示:
给定一个字符串 (s
) 和一个字符模式 (p
) ,实现一个支持 '?'
和 '*'
的通配符匹配。
'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。
说明:
s
可能为空,且只包含从a-z
的小写字母。p
可能为空,且只包含从a-z
的小写字母,以及字符?
和*
。
示例 1:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:
s = "aa"
p = "*"
输出: true
解释: '*' 可以匹配任意字符串。
示例 3:
输入:
s = "cb"
p = "?a"
输出: false
解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。
一、思路
tips:这一题和
力扣第十题-正则表达式匹配
很像,有兴趣的可以看一下那一题
很明显也可以使用 动态规划 来实现,所以第一步我们要确定 状态转移方程
状态转移方程
第一步就是确定边界情况,很显然有以下几种情况:
变量说明 dp[i][j]:表示字符
s
前i
个字符是否可以可p
中前j
个字符可以匹配。(下标从[1, 1]
开始)
p[j]
为字母:dp[i][j] = (s[i] == s[j] && dp[i-1][j-1])
p[j]
为?
:dp[i][j] = dp[i-1][j-1]
p[j]
为*
:dp[i][j] = (dp[i][j-1] || dp[i-1][j])
。(可看下面的详细解释)
解释上面的第三点:
如果
p[j] == '*'
会存在用*
和不用*
的两种情况,举了两个例子如下所示:
s = abcd
,p = abcd*
。此时dp[4][5] 就不需要使用*
s = abcde
,p = abcd*
。此时dp[4][5] 就需要使用*
举个例子
此处以 s = adceb
,p = *a*b
作为例子。(动态规划就像是一个填格子的游戏,从左上角开始,一直往右下的方式填写)
- 因为
p[0] = '*'
,说明以p[0]
开始是可以匹配的,所以需要将dp[0][1]
置为true
i=1, j=1
,此时p[0] = '*'
,根据状态转移方程
可得dp[1][1] = dp[1][0] || dp[0][1] = true
i=1, j=2
,此时p[1] = 'a'
,根据状态转移方程
可得dp[1][2] = dp[0][1] = true
i=1, j=3
,此时p[2] = '*'
,根据状态转移方程
可得dp[1][3] = dp[1][2] || dp[0][3] = true
i=1, j=4
,此时p[3] = 'b'
,此时无法匹配,故dp[1][4] = false
- 后续的遍历基本类似,故不一一列举
- 最终可以得到
dp[5][4] = true
,故返回结果true
即可
填完的表格如下所示:
二、实现
实现代码
实现代码与思路基本保持一致
public boolean isMatch(String s, String p) {
int m = s.length();
int n = p.length();
boolean[][] dp = new boolean[m + 1][n + 1];
dp[0][0] = true;
// 处理p中的前N个 * ,并记录有多少个*
int count = 1;
for (int i = 1; i <= n; ++i) {
if (p.charAt(i - 1) == '*') {
dp[0][i] = true;
count++;
} else {
break;
}
}
// 往右下走
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (p.charAt(j - 1) == '*') {
dp[i][j] = dp[i][j - 1] || dp[i - 1][j];
} else if (p.charAt(j - 1) == '?' || s.charAt(i - 1) == p.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
}
}
}
return dp[m][n];
}
测试代码
public static void main(String[] args) {
new Number44().isMatch("adceb", "*a*b");
}
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥