题目
请实现一个函数用来匹配包含'. '和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但与"aa.a"和"ab*a"均不匹配。
示例 1:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:
输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。
示例 4:
输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:
输入:
s = "mississippi"
p = "mis*is*p*."
输出: false
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母以及字符 . 和 *,无连续的 '*'。
思路:动态规划
动态规划:就是利用历史记录避免重复计算。而这些历史记录,需要一些变量来保存,一般是用一维数组或者二维数组来保存。
动态规划做题三个步骤:
Step1.定义数组元素的含义。
我们会用一个数组,来保存历史记录,一个非常重要的点就是规定这个数组元素的含义。例如 dp[i] 是代表什么意思?
Step2.找出数组元素之间的关系式。动态规划有一点类似于数学归纳法,当要计算 dp[n] 时,可以利用 dp[n-1],dp[n-2]…..dp[1]来推出 dp[n] 。也就是可以利用历史数据来推出新的元素值,所以我们要找出数组元素之间的关系式,例如 dp[n] = dp[n-1] + dp[n-2],这一步,也是最难的一步。
(学过动态规划的可能都经常听到最优子结构:把一个规模比较大的问题分成几个规模比较小的问题,可以从子问题的最优结果推出更大规模问题的最优结果。例如:让你算每个班的最优成绩就是子问题,你知道所有子问题的答案后,就可以借此推出全校学生的最优成绩这个规模更大的问题的答案。)
Step3.找出初始值。
第二步虽然知道了数组元素之间的关系式,例如 dp[n] = dp[n-1] + dp[n-2],可以通过 dp[n-1] 和 dp[n-2] 来计算 dp[n],但得知道初始值啊,例如一直推下去的话,会由 dp[3] = dp[2] + dp[1]。而 dp[2] 和 dp[1] 是不能再分解的了。所以必须要能够直接获得 dp[2] 和 dp[1] 的值,这就是所谓的初始值。(注意初始值的严谨性)有了初始值,并且有了数组元素之间的关系式,那么就可以得到 dp[n] 的值了,而 dp[n] 的含义是由你来定义的,你想求什么,就定义它是什么,这样题目也就解出来了。
1.定义数组元素的含义
dp[i][j]:表示当字符串s和p的长度为i和j时,s与p是否匹配。
我们要求的就是当字符串s长度为n,字符串p长度为m时,s与p是否匹配(false/true)。
2.找出数组元素之间的关系式
通过比较s[i]和p[j],来找出它们之间的关系式。分情况讨论:
①当p[j]是"普通字符"时:
1)当p[j] == s[i]时:dp[i][j] = dp[i - 1][j - 1];
2)当p[j] != s[i]时:dp[i][j] = false;
②当p[j]是" . "(万能符,什么都能匹配)时: dp[i][j] = dp[i - 1][j - 1];(可与①1合并)
③当p[j]是" * "(表示0个或多个前面的字符)时:
1)当p[j - 1] != s[j]时:dp[i][j] = dp[i - 1][j - 2];
2)当p[j - 1] == s[j]时:
- 当" * "匹配0个时:dp[i][j] = dp[i - 1][j - 2];
- 当" * "匹配1个时:dp[i][j] = dp[i - 1][j - 1];
- 当" * "匹配多个时:dp[i][j] = dp[i - 1][j];
3.找出初始值
不能让dp[i][j]越界。
先要找出dp[0][j]的所有值。
dp[0][0] = true;
dp[0][1] = false;
dp[0][j] (j >= 2):
- 当p[j]不是" * "时,为false;
- 当p[j]是" * "时,dp[0][j] = dp[0][j - 2];
代码
class Solution {
public boolean isMatch(String s, String p) {
if(s == null || p == null) {
return true;
}
int n = s.length();
int m = p.length();
boolean[][] dp = new boolean[n + 1][m + 1];
dp[0][0] = true;
for(int j = 2; j <= m; j++) {
if(p.charAt(j - 1) == '*') {
dp[0][j] = dp[0][j - 2];
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) { //此处j = 1不用担心下面j - 2会越界(因为就不会走到下面的逻辑)
//当j不为*
if(p.charAt(j - 1) != '*') {
if(p.charAt(j - 1) == '.' || p.charAt(j - 1) == s.charAt(i - 1)) {
dp[i][j] = dp[i - 1][j - 1];
}
} else {
//第j - 1个字符不匹配
if(p.charAt(j - 2) != s.charAt(i - 1) && p.charAt(j - 2) != '.') {
dp[i][j] = dp[i][j - 2];
} else {
dp[i][j] = dp[i][j - 2] || dp[i][j - 1] || dp[i- 1][j];
}
}
}
}
return dp[n][m];
}
}
本文介绍一种使用动态规划解决正则表达式匹配问题的方法,包括定义数组元素含义、找出数组元素间的关系式及确定初始值等关键步骤。

被折叠的 条评论
为什么被折叠?



