1.题目描述
请实现一个函数用来匹配包含'. '和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但与"aa.a"和"ab*a"均不匹配。
示例1:
输入: s = "aa" p = "a" 输出: false 解释: "a" 无法匹配 "aa" 整个字符串。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof
2.思路分析
2.1 定义dp方法: dp(s, i, p, j)表示s[i,,,]和p[j,,,]是否能匹配成功
2.2 初始化:
- 当p的指针j走到结尾时,如果s的指针i也走到结尾,说明匹配成功;
- 当s的指针i走到结尾时,p剩下的字符必须是字符和*交替出现才能成功,否则匹配失败。
2.3 状态转移方程:
定义两个指针i, j,分别指向字符串s和p,依次从0开始向后匹配
对于任意两个字符,会出现两种情况
第一种:s[ i ] == p[ j ] || s[ i ] == ' . '
此时再分两种情况处理: 先判断当前字符的下一个字符是否为*
- 如果有*,则可以选择匹配0次或者无数次,只要符合任何一种情况都行。
- 如果没有*,老老实实匹配下一个
第二种: s[ i ] != p[ j ]
此时再分两种情况处理: 先判断当前字符的下一个字符是否为*
- 如果有*,只能选择匹配0次。
- 如果没有*,匹配失败。
3.解答
class Solution {
//DP
//使用备忘录,消除重叠子问题
HashMap<String, Boolean> memo = new HashMap<>();
public boolean isMatch(String s, String p) {
return dp(s, 0, p, 0);
}
//辅助方法:dp方法:dp(s, i, p, j)表示s[i,,,]和p[j,,,]是否能匹配成功
public boolean dp(String s, int i, String p, int j){
int m = s.length(), n = p.length();
//base case
//当p的指针j走到结尾时
if(j == n){
//如果s的指针i也走到结尾,说明匹配成功
return i == m;
}
//当s的指针i走到结尾时
if(i == m){
//p剩下的字符必须是字符和*交替出现才能成功,因此剩下的必须是偶数
if((n - j) % 2 == 1){
//如果是奇数,返回false
return false;
}
//进一步判断是否是字符和*交替出现
while(j + 1 < n){
if(p.charAt(j + 1) != '*'){
return false;
}
j += 2;
}
return true;
}
//如果备忘录里有该条记录,直接返回
String key = i + "," + j;
if(memo.containsKey(key)){
return memo.get(key);
}
boolean res = false;
//状态转移
//如果当前字符匹配成功
if(s.charAt(i) == p.charAt(j) || p.charAt(j) == '.'){
//判断当前字符的下一个字符是否为*
if(j + 1 < n && p.charAt(j + 1) == '*'){
//如果有*,则可以选择匹配0次或者无数次,只要符合任何一种情况都行
res = dp(s, i, p, j + 2) || dp(s, i + 1, p, j);
}else{
//如果没有*,老老实实匹配下一个
res = dp(s, i + 1, p, j + 1);
}
}else{
//如果当前字符没有匹配成功
//判断当前字符的下一个字符是否为*
if(j + 1 < n && p.charAt(j + 1) == '*'){
//如果是*,只能选择匹配0次
res = dp(s, i, p, j + 2);
}else{
//如果没有*,匹配失败
res = false;
}
}
//将该条记录添加到备忘录
memo.put(key, res);
return res;
}
}
时间复杂度:O(MN)
空间复杂度:O(MN)