🔥题目
请实现一个函数用来匹配包含’.
‘和’*
‘的正则表达式。
字符’.
‘表示任意一个字符,’*
'表示它前面的字符可以出现任意次(含0次)。
☘️解析
首先不难想到动态规划的含义:dp[i][j]表示原串的前i个字符与匹配串的前j个字符是否匹配(注意i,j都可取0)
明确了dp的含义,下面开始初始化:
1)原串和匹配串都为空串的情况。空串当然可匹配空串,因此有 dp[0][0] = true 。
2)原串为空串,匹配串不为空串的情况。这时竟然也可能匹配!这是因为’*
‘的一个作用就是将前一个字符视为不存在。比如"a*b*c*
"即可匹配空串。这种“将前一个字符视为不存在”的作用可以表示为,当扫描到’*
'时,dp[0][j] = dp[0][j - 2] 。
3)原串不为空串,匹配串为空串的情况。很显然,一定无法匹配。因此有 dp[i][0] = false 。
完成了初始化,下面开始动态转移:
a)对’.
'的处理。这非常简单,因为它可以表示任意一个字符,扫描到’.
'可直接认为该位置的两个字符匹配成功。
b)对’*
'的处理。这比较困难,这里用一个例子来说明。假设原串为"abcccc
",匹配串为"abc*
"。上面已经说明了’*
'的一个作用,即“将前一个字符c视为不存在(作用1)”,这里还要用到它的另一个作用,即“将前一个字符c重复多次,因此可以将原串中与c相同的字符视为不存在(作用2)”。回到例子,首先使用作用2将原串转化为"abccc
"、"abcc
"、"abc
"、"ab
",然后使用作用1将匹配串转化为"ab
",匹配成功!
🧊代码
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;
// 初始化(原串为空串,匹配串不为空串的情况)
for (int j = 2; j <= n; j++) {
if (p.charAt(j - 1) == '*') {
dp[0][j] = dp[0][j - 2];
}
}
// 状态转移
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
char ss = s.charAt(i - 1);
char pp = p.charAt(j - 1);
if (ss == pp || pp == '.') {
dp[i][j] = dp[i - 1][j - 1];
} else if (pp == '*') {
dp[i][j] = ((p.charAt(j - 2) == ss || p.charAt(j - 2) == '.') && dp[i - 1][j]) || dp[i][j - 2];
}
}
}
return dp[m][n];
}
}
🌸补充
这里再补充一个类似的题目,所使用通配符的含义有所不同。
请实现一个函数用来匹配包含’?
‘和’*
‘的正则表达式。
字符’?
‘表示任何单个字符,’*
'表示任意字符串(包括空字符串)。
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];
// s和p都为空串
dp[0][0] = true;
// s为空串,p不为空串
for (int j = 1; j <= n; j++) {
if (p.charAt(j - 1) == '*') {
dp[0][j] = dp[0][j - 1];
}
}
// 状态转移
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
char ss = s.charAt(i - 1);
char pp = p.charAt(j - 1);
if (ss == pp || pp == '?') {
dp[i][j] = dp[i - 1][j - 1];
} else if (pp == '*') {
dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
}
}
}
return dp[m][n];
}
}