19. 正则表达式匹配
1 题目描述
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
‘.’ 匹配任意单个字符
‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
2 题目分析
假设主串为 A,模式串为 B 从最后一步出发,需要关注最后进来的字符。假设 A 的长度为 n ,B 的长度为 m ,关注正则表达式 B 的最后一个字符是谁,它有三种可能,正常字符、* 和 .(点),那针对这三种情况讨论即可,如下:
-
如果 B 的最后一个字符是正常字符,那就是看 A[n-1] 是否等于 B[m-1],相等则看 A_{0…n-2}与 B_{0…m-2},不等则是不能匹配,这就是子问题。
-
如果 B 的最后一个字符是.,它能匹配任意字符,直接看 A{0…n-2}与 B{0…m-2}
-
如果 B 的最后一个字符是*它代表 B[m-2]=c 可以重复0次或多次,它们是一个整体 c∗
-
情况一:A[n-1]是 0 个 c,B 最后两个字符废了,能否匹配取决于 A_{0…n-1}和 B_{0…m-3}是否匹配
-
情况二:A[n-1] 是多个 c中的最后一个(这种情况必须 A[n-1]=c或者 c=’.’),所以 A 匹配完往前挪一个,B继续匹配,因为可以匹配多个,继续看 A{0…n-2}和 B{0…m-1}是否匹配。
转移方程
f[i] [j]代表 A 的前 i个和 B 的前 j 个能否匹配
对于前面两个情况,可以合并成一种情况 f[i] [j] = f[i-1] [j-1]
对于第三种情况,对于 c*分为看和不看两种情况
不看:直接砍掉正则串的后面两个, f[i] [j] = f[i] [j-2]
看:正则串不动,主串前移一个,f[i] [j] = f[i-1] [j]
初始条件
特判:需要考虑空串空正则
-
空串和空正则是匹配的,f[0] [0] = true
-
空串和非空正则,不能直接定义 true 和 false,必须要计算出来。(比如A= ‘’ ‘’ ,B=a* b *c *)
-
非空串和空正则必不匹配,f[1] [0]=…=f[n] [0]=false
-
非空串和非空正则,那肯定是需要计算的了。
大体上可以分为空正则和非空正则两种,空正则也是比较好处理的,对非空正则我们肯定需要计算,非空正则的三种情况,前面两种可以合并到一起讨论,第三种情况是单独一种,那么也就是分为当前位置是 * 和不是 *两种情况了。
作者:jerry_nju
3 代码
class Solution {
public boolean isMatch(String A, String B) {
int n = A.length();
int m = B.length();
boolean[][] f = new boolean[n + 1][m + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
//分成空正则和非空正则两种
if (j == 0) {
f[i][j] = i == 0;
} else {
//非空正则分为两种情况 * 和 非*
if (B.charAt(j - 1) != '*') {
if (i > 0 && (A.charAt(i - 1) == B.charAt(j - 1) || B.charAt(j - 1) == '.')) {
f[i][j] = f[i - 1][j - 1];
}
} else {
//碰到 * 了,分为看和不看两种情况
//不看
if (j >= 2) {
f[i][j] |= f[i][j - 2];
}
//看
if (i >= 1 && j >= 2 && (A.charAt(i - 1) == B.charAt(j - 2) || B.charAt(j - 2) == '.')) {
f[i][j] |= f[i - 1][j];
}
}
}
}
}
return f[n][m];
}
}