文章目录
- 正则表达式匹配
- 描述
- 示例1
- 示例2
- 示例3
- 示例4
- 思路
- 完整代码
正则表达式匹配
描述
请实现一个函数用来匹配包括’.‘和’*'的正则表达式。
1.模式中的字符’.'表示任意一个字符
2.模式中的字符’*'表示它前面的字符可以出现任意次(包含0次)。
在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 "aaa"
与模式"a.a"和 "ab*ac*a"
匹配,但是与 "aa.a"
和 "ab*a"
均不匹配
数据范围:
1.str 只包含从 a-z 的小写字母。
2.pattern 只包含从 a-z 的小写字母以及字符 .
和 *
,无连续的 '*'
。
- $0≤str.length≤26 $
- 0 ≤ p a t t e r n . l e n g t h ≤ 26 0≤pattern.length≤26 0≤pattern.length≤26
示例1
输入:
"aaa","a*a"
返回值:
true
说明:
中间的*可以出现任意次的a,所以可以出现1次a,能匹配上
示例2
输入:
"aad","c*a*d"
返回值:
true
说明:
因为这里 c 为 0 个,a被重复一次, * 表示零个或多个a。因此可以匹配字符串 "aad"。
示例3
输入:
"a",".*"
返回值:
true
说明:
".*" 表示可匹配零个或多个('*')任意字符('.')
示例4
输入:
"aaab","a*a*a*c"
返回值:
false
思路
一个最最最简单的办法就是使用String提供的match方法,可以直接匹配正则表达式,并返回true或false,至少对于题目提供的案例都能通过
public boolean match (String str, String pattern) {
// write code here
return str.matches(pattern);
}
但这题考点主要是动态规划,参考官方答案
- 设
dp[i][j]
表示str前i个字符和pattern前j个字符是否匹配。(需要注意这里的i,j是长度,比对应的字符串下标要多1) - (初始条件) 首先,毋庸置疑,两个空串是直接匹配,因此
d
p
[
0
]
[
0
]
=
t
r
u
e
dp[0][0]=true
dp[0][0]=true。然后我们假设str字符串为空,那么pattern要怎么才能匹配空串呢?答案是利用
'*'
字符出现0次的特性。遍历pattern字符串,如果遇到'*'
意味着它前面的字符可以出现0次,要想匹配空串也只能出现0,那就相当于考虑再前一个字符是否能匹配,因此 d p [ 0 ] [ i ] = d p [ 0 ] [ i − 2 ] dp[0][i]=dp[0][i-2] dp[0][i]=dp[0][i−2] - (状态转移) 然后分别遍历str与pattern的每个长度,开始寻找状态转移。首先考虑字符不为
'*'
的简单情况,只要遍历到的两个字符相等,或是pattern串中为’.'即可匹配,因此最后一位匹配,即查看二者各自前一位是否能完成匹配,即 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] dp[i][j]=dp[i-1][j-1] dp[i][j]=dp[i−1][j−1]。然后考虑'*'
出现的情况:pattern[j - 2] == '.' || pattern[j - 2] == str[i - 1]
:即pattern前一位能够多匹配一位,可以用'*'
让它多出现一次或是不出现,因此有转移方程 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ∣ ∣ d p [ i ] d p [ j − 2 ] dp[i][j]=dp[i-1][j]||dp[i]dp[j-2] dp[i][j]=dp[i−1][j]∣∣dp[i]dp[j−2]- 不满足上述条件,只能不匹配,让前一个字符出现0次, d p [ i ] [ j ] = d p [ i ] [ j − 2 ] dp[i][j]=dp[i][j-2] dp[i][j]=dp[i][j−2]
完整代码
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @param pattern string字符串
* @return bool布尔型
*/
public boolean match (String str, String pattern) {
// write code here
int n1 = str.length();
int n2 = pattern.length();
//dp[i][j]表示str前i个字符和pattern前j个字符是否匹配
boolean[][] dp = new boolean[n1 + 1][n2 + 1];
//遍历str每个长度
for (int i = 0; i <= n1; i++) {
//遍历pattern每个长度
for (int j = 0; j <= n2; j++) {
//空正则的情况
if (j == 0) {
dp[i][j] = (i == 0 ? true : false);
//非空的情况下 星号、点号、字符
} else {
if (pattern.charAt(j - 1) != '*') {
//当前字符不为*,用.去匹配或者字符直接相同
if (i > 0 && (str.charAt(i - 1) == pattern.charAt(j - 1) ||
pattern.charAt(j - 1) == '.')) {
dp[i][j] = dp[i - 1][j - 1];
}
} else {
//碰到*
if (j >= 2) {
dp[i][j] |= dp[i][j - 2];
}
//若是前一位为.或者前一位可以与这个数字匹配
if (i >= 1 && j >= 2 && (str.charAt(i - 1) == pattern.charAt(j - 2) ||
pattern.charAt(j - 2) == '.')) {
dp[i][j] |= dp[i - 1][j];
}
}
}
}
}
return dp[n1][n2];
}
}
这里需要按位进行或操作,不清楚为什么