正则表达式匹配

请实现一个函数用来匹配包括’.‘和’ * ‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab * ac * a"匹配,但是与"aa.a"和"ab * a"均不匹配

一、剑指offer

链接
分析:
理解题意:文本串中没有.和,但在正则表达式模式串中有。*
首先,考虑特殊情况:

  • 两个字符串都为空,返回true
  • 当第一个字符串不空,而第二个字符串空了,返回false(因为这样,就无法匹配成功了,而如果第一个字符串空了,第二个字符串非空,还是可能匹配成功的,比如第二个字符串是“aaaa”,由于‘*’之前的元素可以出现0次,所以有可能匹配成功)

当模式中的第二个字符不是“*”时:

  • 如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模式都后移一个字符,然后匹配剩余的。
  • 如果 字符串第一个字符和模式中的第一个字符相不匹配,直接返回false。

而当模式中的第二个字符是“*”时

(1)如果字符串第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继续匹配。(x*表示0个x)
(2)如果字符串第一个字符跟模式第一个字符匹配,可以有3种匹配方式:

  • 模式后移2字符,相当于x被忽略;(x表示0个x)
  • 文本串后移1字符,模式串后移2字符;(x*表示一个x)
  • 有限状态机不变:文本串后移1字符,模式串不变,即继续匹配字符下一位,因为*可以匹配多位;( x *表示多个x,包含上一种情况)

剑指参考答案

class Solution{
public:
	bool match(const char* str, const char* pattern)
	{
	    if(str == nullptr || pattern == nullptr)
	        return false;
	
	    return matchCore(str, pattern);
	}
	
	bool matchCore(const char* str, const char* pattern)
	{
	    if(*str == '\0' && *pattern == '\0')
	        return true;
	
	    if(*str != '\0' && *pattern == '\0')
	        return false;
	
	    if(*(pattern + 1) == '*')
	    {
	        if(*pattern == *str || (*pattern == '.' && *str != '\0'))
	            // 进入有限状态机的下一个状态
	            return matchCore(str + 1, pattern + 2)
	            // 继续留在有限状态机的当前状态 
	            || matchCore(str + 1, pattern)
	            // 略过一个'*' 
	            || matchCore(str, pattern + 2);
	        else
	            // 略过一个'*'
	            return matchCore(str, pattern + 2);
	    }
		
		//省略了else命令
	    if(*str == *pattern || (*pattern == '.' && *str != '\0'))
	        return matchCore(str + 1, pattern + 1);
	
	    return false;
	}

Reference

https://blog.youkuaiyun.com/u012429555/article/details/90110128?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-7.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-7.nonecase

二、备忘录递归-由顶向下

链接
https://labuladong.online/algo/dynamic-programming/regular-expression-matching/

class Solution {
    public:
    // 备忘录
    vector<vector<int>> memo;

    bool isMatch(string s, string p) {
        int m = s.size(), n = p.size();
        // memo[i][j]: s[i...]和p[j...]匹配的结果记录
        memo = vector<vector<int>>(m, vector<int>(n, -1));
        // 指针 i,j 从索引 0 开始移动
        // dp(s,i,p,j): s[i...]和p[j...]是否能够匹配
        return dp(s, 0, p, 0);
    }

    // 计算 p[j..] 是否匹配 s[i..]
    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;
        }

        // 查备忘录,防止重复计算
        if (memo[i][j] != -1) {
            return memo[i][j];
        }

        bool res = false;

        if (s[i] == p[j] || p[j] == '.') {
            if (j < n - 1 && p[j + 1] == '*') {
                res = dp(s, i, p, j + 2) // 有通配符'*', 匹配0个
                        || 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);  // 有通配符'*', 匹配0个
            } else {
                res = false; 
            }
        }
        // 将当前结果记入备忘录
        memo[i][j] = res;
        return res;
    }
};

三、动态规划-由底向上

class Solution {
public:
    bool isMatch(string s, string p) {
        int m = s.length(), n = p.length();
        // dp[i][j] 表示 s[i..] 是否匹配 p[j..]
        vector<vector<bool>> dp(m+1, vector<bool>(n+1, false));

        // base case:
        // 空串匹配空串
        dp[m][n] = true;
        // s[i..] 匹配空串为 false
        for(int i = 0; i < m; i++) {
            dp[i][n] = false;
        }
        // 空串匹配 p[j..],主要看 p[j..] 是否是 x*z*y*.. 形式
        for(int j = 0; j < n; j++) {
            if((n-j) % 2 == 1)
                dp[m][j] = false;
            else {
                bool res = true;
                for(int jj = j; jj < n-1; jj += 2)
                    if(p[jj+1] != '*')
                        res = false;
                dp[m][j] = res;
            }
        }

        // 从后往前,dp[i][j] 需要的是其后面的元素
        for(int i = m-1; i >= 0; i--) {
            for(int j = n-1; j >= 0; j--) {
                if(s[i] == p[j] || p[j] == '.') {
                    // “j+1 < n“ ,因为 n 此时有效,表示末尾的空串,需要考虑。
                    if(j+1 < n && p[j+1] == '*') {
                        dp[i][j] = dp[i][j+2] || dp[i+1][j];
                    }else {
                        dp[i][j] = dp[i+1][j+1];
                    }
                } else {
                    // 同上
                    if(j+1 < n && p[j+1] == '*') {
                        dp[i][j] = dp[i][j+2];
                    }else {
                        dp[i][j] = false;
                    }
                }
            }
        }
        
        return dp[0][0];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值