题解
- 题意:给定一个正常字符串
S
以及一个正则字符串P
,判断两者是否表示相同的字符串,其中S
只包含普通字符,P
中可能包含三种类型字符,分别是普通字符, .(匹配任意一个普通字符), *(匹配前面一个字符任意次,包括0次) - 题解: 分类讨论, 我们利用
i
, j
表示S和P当前位置的字符,假设两者长度分别是n
和m
- 若
P[j] != . && p[j] != *
,那么判断S[0~i]==P[0~j]
就转换为S[0~i-1]
和P[0~j-1]
,否则说明直到当前位置的两个字符串是不匹配的 - 若
P[j]=='.'
那么,当前S[i]
无论是什么,都是匹配的,因此转换为S[0~i-1]
和P[0~j-1]
的对比,不难发现,其实可以包含进第一种情况 - 最难搞的就是
P[j]==*
, 此时我们不能直接拿P[j]
与S[i]
比较,而是要把P[j-1]P[j]
视为一个整体,为了方便起见,我们使用c
代表P[j-1]
。
- 此时我们要判读那
S[i]
是否是c
- 如果是,那么就检查
S[0~i-1]
和P[0~j]
是否仍然匹配 - 如果不是,那么只有当
c*
取0次的时候才可能匹配,这样一来,问题就转换成S[0~i]
和P[0~j-2]
的匹配。
- 状态表示: 和其他字符串动态规划问题一样,也是利用一个二维数组
dp[i][j]
代表S[0~i]
和P[0~j]
的匹配情况,最后的情况就是dp[n][m]
- 初始状态: 这里有比较多明确的初试状态需要考虑
dp[0][0]
:无疑是正确的,因为这是空对空dp[0][x>0]
:需要判断,因为P[0~x]
可能匹配空字符串,如a*
可以代表取0次a
dp[x>0][0]
:无疑是错误的,因为S
是普通字符串,无法匹配空字符串
- 状态转移:
- 情况1,2:
dp[i][j] = dp[i-1][j-1] && (S[i]==P[j])
- 情况3:
S[i] != P[j-1]
: dp[i][j] == dp[i][j-2]
S[i] == P[j-1]
: dp[i][j] == dp[i-1][j]
- 实现:序列dp小技巧,在字符串前面加上一个
' '
,省去对边界的考虑
class Solution {
public boolean isMatch(String s, String p) {
s = ' '+s;
p = ' '+p;
int n = s.length();
int m = p.length();
boolean[][] dp = new boolean[1+n][1+m];
char[] sc = s.toCharArray();
char[] pc = p.toCharArray();
dp[0][0] = true;
for(int i = 1;i < n;i++){
dp[i][0] = false;
}
for(int j = 2;j < m;j+=2){
if(pc[j] == '*') dp[0][j] = dp[0][j-2];
}
for(int i = 1;i < n;i++){
for(int j = 1;j < m;j++){
if(pc[j]!='*'){
if(pc[j] == '.' || pc[j]==sc[i])
dp[i][j] = dp[i-1][j-1];
}
else{
if(j >= 2) dp[i][j] |= dp[i][j-2];
if(pc[j-1] == sc[i]||pc[j-1]=='.') dp[i][j] |= dp[i-1][j];
}
}
}
return dp[n-1][m-1];
}
}
题目