今天做到LeetCode中的第十题:正则表达式匹配,难度级别为困难,匹配情况复杂,思路不难理解,但是仍无从下手,暂且先记录一下,改日再做研究理解。
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
- ‘.’ 匹配任意单个字符
- ‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
解题思路:
bool dp(string& s , int i,string& p,int j){
int m = s.size(), n = p.size();
//base case
if(j == n){
return i == m;
}
if(i == m){
if((n - j) % 2 ==1){
return false;
}
for(; j + 1 < n; j += 2){
if(p[j + 1] != '*'){
return false;
}
}
return true;
}
//记录状态(i,j),消除重叠子问题
string key = to_string(i) + "," + to_string(j);
if(memo.count(key)){
return memo[key];
}
bool res = false;
if(s[i] == p[j] || p[j] == '.'){
if(j < n-1 && p[j+1] == '*'){
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 < n-1 && p[j + 1] == '*'){
res=dp(s, i, p, j+2);
}else{
res = false;
}
}
//将结果记入备忘录
memo[key] = res;
return res;
}
public static boolean isMatch(String s,String p) {
if(s==null||p==null) {
return false;
}
int sLen=s.length(),pLen=p.length();
boolean[][] dp=new boolean[sLen+1][pLen+1];
dp[0][0]=true;
//"aab","c*a*b"
for(int j=1;j<pLen+1;j++) {
if(p.charAt(j-1)=='*')dp[0][j]=dp[0][j-2];
}
for(int i=1;i<sLen+1;i++) {
for(int j=1;j<pLen+1;j++) {
if(s.charAt(i-1)==p.charAt(j-1)||p.charAt(j-1)=='.') {
dp[i][j]=dp[i-1][j-1];
}else if(p.charAt(j-1)=='*') {
if(s.charAt(i-1)==p.charAt(j-2)||p.charAt(j-2)=='.') {
dp[i][j]=dp[i][j-2]||dp[i-1][j-2]||dp[i-1][j];
}else {
dp[i][j]=dp[i][j-2];
}
}
}
}
return dp[sLen][pLen];
}
参考答案:力扣题解
class Solution {
public boolean isMatch(String s, String p) {
return recur(s.toCharArray(),p.toCharArray(),s.length()-1,p.length()-1);
}
public boolean recur(char[] ss,char[] pp,int sidx,int pidx){
//结束条件确实恶心,各种idxOutBound
if(pidx<0){
return sidx==pidx;
}
if(sidx<0){
while(pidx>=0&&pp[pidx]=='*'){//bug_注意:pidx>=0
pidx-=2;
}
return pidx==sidx;
}
//下面逻辑代码不需要考虑idx为负数,就算idx负数,那也是下一个recur去判断.
if(pp[pidx]!='*'){//不是*好说,要么点,要么字母
if(pp[pidx]=='.'){//是点,点可以匹配任意,所以直接recur
return recur(ss,pp,sidx-1,pidx-1);
}else{
if(pp[pidx]==ss[sidx]){//s和p的末尾字母,若相同,也是直接recur
return recur(ss,pp,sidx-1,pidx-1);
}else{
return false;//s和p的末尾字母,不相同直接gg,返false
}
}
}else{
//根据题意:*匹配0个or多个
if(recur(ss,pp,sidx,pidx-2)) return true;//这是:*匹配0个
char ctemp=pp[pidx-1];//下面是*匹配多个
int sidxmove=sidx;
/**
* 用p的末尾去匹配s的末尾的1个,2个,3个,4个...不可能匹配掉无限个吧!!用*把ss[0]都匹配掉了应该是极限了
*/
while(sidxmove>=0&&(ctemp=='.'||ctemp==ss[sidxmove])){//用*匹配掉1个,2个,3个,4个...
if(recur(ss,pp,sidxmove-1,pidx-2)){
return true;//加速加速
}
sidxmove--;
}
return false;//上面加速都不成功,说明没救了
}
}
}
class Solution {
public boolean isMatch(String ss, String pp) {
// 技巧:往原字符头部插入空格,这样得到 char 数组是从 1 开始,而且可以使得 f[0][0] = true,可以将 true 这个结果滚动下去
int n = ss.length(), m = pp.length();
ss = " " + ss;
pp = " " + pp;
char[] s = ss.toCharArray();
char[] p = pp.toCharArray();
// f(i,j) 代表考虑 s 中的 1~i 字符和 p 中的 1~j 字符 是否匹配
boolean[][] f = new boolean[n + 1][m + 1];
f[0][0] = true;
for (int i = 0; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// 如果下一个字符是 '*',则代表当前字符不能被单独使用,跳过
if (j + 1 <= m && p[j + 1] == '*') continue;
// 对应了 p[j] 为普通字符和 '.' 的两种情况
if (i - 1 >= 0 && p[j] != '*') {
f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
}
// 对应了 p[j] 为 '*' 的情况
else if (p[j] == '*') {
f[i][j] = (j - 2 >= 0 && f[i][j - 2]) || (i - 1 >= 0 && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.'));
}
}
}
return f[n][m];
}
}
参考答案:添加链接描述