这是我参与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] = truei=1, j=2,此时p[1] = 'a',根据状态转移方程可得dp[1][2] = dp[0][1] = truei=1, j=3,此时p[2] = '*',根据状态转移方程可得dp[1][3] = dp[1][2] || dp[0][3] = truei=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");
}
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥