题目描述
请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配
方法一:递归
自顶向下:
假设主串为s,长度为sn,模式串为p,长度为pn,位于模式串p当前的第i位来说,有‘正常字符’、‘*’、‘.’三种情况。
- 如果p[i]为正常字符,那么我们看s[i]是否等于p[i],如果相等,说明第i位匹配成功,接下来看s[i+1…sn+1]和p[i+1…pn+1]
- 如果p[i]为‘.’,它能匹配任意字符,直接看s[i+1…sn+1]和p[i+1…pn+1]
- 如果p[i]为’*’,表明p[i-1]可以重复0次或者多次,需要把p[i-1]和p[i]看成一个整体。
- 如果p[i-1]重复0次,则直接看s[i…sn-1]和p[i+2…pn-1]
- 如果p[i-1]重复1次或多次,则直接看s[i+1…sn-1]和p[i…pn-1], 但是有个前提:s[i]= = p[i]或者p[i]= = ‘.’
那么可以递归进行计算:
递归三部曲:
- 递归函数功能:match(s, p) -> bool,表示p是否可以匹配s
- 递归终止条件:
- 如果s和p同时为空,表明正确匹配
- 如果s不为空,p为空,表明不能正确匹配
- 如果s为空,p不为空,需要计算,不能直接给出结果。
- 下一步递归:
- 对于前面讨论的情况1,2进行合并,如果 s==p || p == ‘.’,则match(s+1,p+1);
- 对于情况3,如果重复一次或者多次,则match(s+1, p),如果重复0次,则match(s, p+2);
方法二:动态规划
动态规划,自底向上
- 动态规划转移方程: f[i][j]表示s的前i个P的前j个能匹配。
- 对于方法一的1,2两种情况可知,f[i][j] = f[i-1][j-1]
- 如果p[i]为正常字符,那么我们看s[i]是否等于p[i],如果相等,说明第i位匹配成功,接下来看s[i+1…sn+1]和p[i+1…pn+1]
- 如果p[i]为‘.’,它能匹配任意字符,直接看s[i+1…sn+1]和p[i+1…pn+1]
- 对于第3种情况可知:
- 如果重复0次,f[i][j] = f[i][j-2]
- 如果重复1次或多次,f[i][j] = f[i-1]f[j]
- 动态规划初始条件:
- s为空且p为空,为真:f[0][0] = 1
- s不为空且p为空,为假:f[1…sn][0] = 0
public boolean match(char[] A, char[] B){
int n = A.length;
int m = B.length;
boolean[][] f = new boolean[n + 1][m + 1];
if(n==0 && m==0) return true;
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[j - 1] != '*') {
if (i > 0 && (A[i - 1] == B[j - 1] || B[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[i - 1] == B[j-2] || B[j-2] == '.')) {
f[i][j] |= f[i - 1][j];
}
}
}
}
}
return f[n][m];
}