解题思路:
设 s s s 的长度为 n n n , p p p 的长度为 m m m ;将 s s s 的第 i i i 个字符记为 s i s_i si, p p p 的第 j j j个字符记为 p j p_j pj,将 s s s 的前 i i i 个字符组成的子字符串记为 s [ : i ] s[:i] s[:i] , 同理将 p p p 的前 j j j 个字符组成的子字符串记为 p [ : j ] p[:j] p[:j] 。
因此,本题可转化为求 s [ : n ] s[:n] s[:n]是否能和 p [ : m ] p[:m] p[:m] 匹配。
- 状态数组: 设二维数组
dp[m + 1][n + 1]
,m和n是s、p的长度- 特殊说明:
dp[i][j]
表示s下标为s[i-1]
的字符,p下标为p[j-1]
字符
- 特殊说明:
- 初始化:
dp[i][j]
表示 s的前i
个字符和p的前j
个字符是否匹配dp[0][0] = true
,表示s和p的前0个字符均为空串肯定匹配dp[0][j] = dp[0][j - 2] 且 p[j - 1] = '*'
: 首行 s 为空字符串,因此当 p 的偶数位为'*'
时才能够匹配(即让 p 的奇数位出现 0 次,保持 p 是空字符串)。因此,循环遍历字符串 p ,步长为 2(即只看偶数位)。
- 状态转移:
p.charAt(j - 1) == '*'
,有三种情况匹配dp[i][j - 2]
,既是p[j - 2]
出现0次(dp[i - 1][j] && s.charAt(i - 1) == p.charAt(j - 2)
,p[j - 2]
出现1次 且 当前i - 1
和j - 2
指向的字符相同dp[i - 1][j] && p.charAt(j - 2) == '.'
,最特殊情况:p[j - 2] = '.',p[j - 1] = '*'
时,根据条件知是万能匹配
p.charAt(j - 1) != '*'
,有两种情况匹配dp[i - 1][j - 1] && s.charAt(i - 1) == p.charAt(j - 1)
,前面元素之前都匹配 且 当前元素也相容dp[i - 1][j - 1] && p.charAt(j - 1) == '.'
,前面元素之前都匹配 且 p的当期元素是'.'
- 返回值:
dp[m][n]
public class Solution {
// 最直观版
public boolean isMatch(String s, String p) {
int m = s.length();
int n = p.length();
// dp[i][j]表示s前i-1个字符,p前j-1个字符是否匹配
boolean[][] dp = new boolean[m + 1][n + 1];
// dp[0][0] :s前0个字符和p的前0个字符默认是空串=匹配
// 注意:由于多了[0][0],所以dp[i][j],定位到的是s[i-1]和p[j-1]的字符
dp[0][0] = true;
// 初始化首行:当s为空串,p的偶数位为*才能匹配
for (int j = 2; j < n + 1; j += 2) {
dp[0][j] = dp[0][j - 2] && p.charAt(j - 1) == '*';
}
// 状态转移
for (int i = 1; i < m + 1; i++) {
for (int j = 1; j < n + 1; j++) {
// 当p[j-1]=*时,有三种情况
if (p.charAt(j - 1) == '*') {
if (dp[i][j - 2]) {// p[j-2]出现0次,i和j指向字符的长度均相同
dp[i][j] = true;
} else if (dp[i - 1][j] && s.charAt(i - 1) == p.charAt(j - 2)) {// p[j-2]出现1次 且 当前i-1和j-2指向的字符相同
dp[i][j] = true;
} else if (dp[i - 1][j] && p.charAt(j - 2) == '.') {// 最特殊情况:p[j-2]=. p[j-1]=*时 是万能匹配
dp[i][j] = true;
}
} else {// 当p[j-1]!=*时,有两种情况
if (dp[i - 1][j - 1] && s.charAt(i - 1) == p.charAt(j - 1)) {// 前面元素之前都匹配 且 当前元素也相容
dp[i][j] = true;
} else if (dp[i - 1][j - 1] && p.charAt(j - 1) == '.') { // 前面元素之前都匹配 且 p的当期元素是.
dp[i][j] = true;
}
}
}
}
return dp[m][n];
}
// 用三目运算符优化使代码更美观,更快
public boolean isMatch1(String s, String p) {
int m = s.length();
int n = p.length();
// dp[i][j]表示s前i-1个字符,p前j-1个字符是否匹配
boolean[][] dp = new boolean[m + 1][n + 1];
// dp[0][0] :s前0个字符和p的前0个字符默认是空串=匹配
// 注意:由于多了[0][0],所以dp[i][j],定位到的是s[i-1]和p[j-1]的字符
dp[0][0] = true;
// 初始化首行:当s为空串,p的偶数位为*才能匹配
for (int j = 2; j < n + 1; j += 2) {
dp[0][j] = dp[0][j - 2] && p.charAt(j - 1) == '*';
}
// 状态转移
for (int i = 1; i < m + 1; i++) {
for (int j = 1; j < n + 1; j++) {
dp[i][j] = p.charAt(j - 1) == '*' ?
dp[i][j - 2] || dp[i - 1][j] && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.') :
dp[i - 1][j - 1] && (p.charAt(j - 1) == '.' || s.charAt(i - 1) == p.charAt(j - 1));
}
}
return dp[m][n];
}
}