一、题目
给你一个输入字符串 (s) 和一个字符模式 (p) ,请你实现一个支持 '?' 和 '*' 匹配规则的通配符匹配:
'?'可以匹配任何单个字符。'*'可以匹配任意字符序列(包括空字符序列)。
判定匹配成功的充要条件是:字符模式必须能够 完全匹配 输入字符串(而不是部分匹配)。
示例 1:
输入: s = "aa", p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:
输入: s = "aa", p = "*"
输出: true
解释: '*' 可以匹配任意字符串。
示例 3:
输入: s = "cb", p = "?a"
输出: false
解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。
提示:
0 <= s.length, p.length <= 2000s仅由小写英文字母组成p仅由小写英文字母、'?'或'*'组成
二、解答
题目分析
本题要求实现一个通配符匹配算法,给定输入字符串 s 和字符模式 p,需判断 p 是否能完全匹配 s。其中,? 可匹配任意单个字符,* 能匹配任意字符序列(包含空字符序列)。匹配必须是完全匹配,而非部分匹配。
示例分析
-
示例 1:
- 输入:
s = "aa",p = "a" - 输出:
false - 解释:模式
"a"只能匹配一个字符a,而输入字符串"aa"有两个字符a,所以模式无法匹配整个字符串。
- 输入:
-
示例 2:
- 输入:
s = "aa",p = "*" - 输出:
true - 解释:模式中的
*可匹配任意字符序列,因此能匹配输入字符串"aa"。
- 输入:
-
示例 3:
- 输入:
s = "cb",p = "?a" - 输出:
false - 解释:模式中的
?可匹配字符串中的c,但模式中的a无法匹配字符串中的b,所以匹配失败。
- 输入:
代码(Java)
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;
for (int i = 1; i <= n; ++i) {
if (p.charAt(i - 1) == '*') {
dp[0][i] = true;
} 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];
}
}
代码分析
整体功能概述
这段代码实现了一个支持 ? 和 * 通配符匹配的算法,用于判断给定的字符串 s 是否能被模式字符串 p 完全匹配。? 可以匹配任意单个字符,* 可以匹配任意字符序列(包括空字符序列)。代码采用动态规划的方法来解决这个问题。
代码结构和变量定义
java
class Solution {
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;
m和n分别存储字符串s和模式字符串p的长度。dp是一个二维布尔数组,dp[i][j]表示字符串s的前i个字符和模式字符串p的前j个字符是否匹配。dp数组的大小为(m + 1) x (n + 1),这样可以方便处理空字符串的情况。dp[0][0]初始化为true,因为空字符串和空模式是匹配的。
初始化 dp 数组的第一行
java
for (int i = 1; i <= n; ++i) {
if (p.charAt(i - 1) == '*') {
dp[0][i] = true;
} else {
break;
}
}
- 这部分代码用于初始化
dp数组的第一行。当字符串s为空时,如果模式字符串p以连续的*开头,那么这些*可以匹配空字符串,所以对应的dp[0][i]为true。一旦遇到非*字符,就停止初始化,因为非*字符无法匹配空字符串。
填充 dp 数组
java
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];
}
}
}
-
这是两层嵌套的循环,用于填充
dp数组的其余部分。-
当
p.charAt(j - 1) == '*'时:dp[i][j - 1]表示*匹配空字符串,即忽略当前的*,看s的前i个字符和p的前j - 1个字符是否匹配。dp[i - 1][j]表示*匹配s的当前字符,即*继续匹配更多的字符,看s的前i - 1个字符和p的前j个字符是否匹配。- 只要这两种情况中有一个为
true,则dp[i][j]为true。
-
当
p.charAt(j - 1) == '?' || s.charAt(i - 1) == p.charAt(j - 1)时:- 表示当前字符匹配,那么
dp[i][j]的值取决于dp[i - 1][j - 1],即s的前i - 1个字符和p的前j - 1个字符是否匹配。
- 表示当前字符匹配,那么
-
返回结果
java
return dp[m][n];
- 最终返回
dp[m][n],表示字符串s的全部字符和模式字符串p的全部字符是否匹配。
总结
问题核心
该 Java 代码要解决的核心问题是实现一个支持 ? 和 * 通配符匹配的算法,判断给定字符串 s 能否被模式字符串 p 完全匹配。其中,? 可匹配任意单个字符,* 能匹配任意字符序列(包含空字符序列)。
实现思路
采用动态规划的方法来解决此问题。通过创建一个二维布尔数组 dp,dp[i][j] 表示字符串 s 的前 i 个字符和模式字符串 p 的前 j 个字符是否匹配。
代码步骤
-
变量和数组初始化:获取字符串
s和模式字符串p的长度m和n,创建(m + 1) x (n + 1)的二维布尔数组dp,并将dp[0][0]初始化为true,表示空字符串和空模式匹配。 -
初始化
dp数组第一行:当字符串s为空时,若模式字符串p以连续的*开头,对应dp[0][i]为true,遇到非*字符则停止初始化。 -
填充
dp数组:使用两层嵌套循环遍历s和p的每个字符,根据不同情况更新dp[i][j]的值。- 当
p中当前字符为*时,dp[i][j]取决于dp[i][j - 1](*匹配空字符串)或dp[i - 1][j](*匹配s当前字符)。 - 当
p中当前字符为?或与s中当前字符相同时,dp[i][j]取决于dp[i - 1][j - 1]。
- 当
-
返回结果:返回
dp[m][n],表示s和p是否完全匹配。
复杂度情况
- 时间复杂度:O(m∗n),因为需要填充一个大小为 (m+1)x(n+1) 的二维数组,其中 m 是字符串
s的长度,n 是模式字符串p的长度。 - 空间复杂度:O(m∗n),主要用于存储
dp数组。