LeetCode算法系列:10. Regular Expression Matching

目录

题目描述

算法

1、回溯法

算法描述

算法实现

2、DP(动态规划法)

算法描述

算法实现


题目描述

Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*'.

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

Note:

  • s could be empty and contains only lowercase letters a-z.
  • p could be empty and contains only lowercase letters a-z, and characters like . or *.

Example 1:

Input:
s = "aa"
p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".

Example 2:

Input:
s = "aa"
p = "a*"
Output: true
Explanation: '*' means zero or more of the precedeng element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".

Example 3:

Input:
s = "ab"
p = ".*"
Output: true
Explanation: ".*" means "zero or more (*) of any character (.)".

Example 4:

Input:
s = "aab"
p = "c*a*b"
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore it matches "aab".

Example 5:

Input:
s = "mississippi"
p = "mis*is*p*."
Output: false

算法

1、回溯法

算法描述

考虑到*可以重复前面字符任意次的特性,如果从前向后进行匹配我们很难确定*表示的重复次数,因此我们从后面往前面进行匹配。


对于简单的情况如果s[i]和p[j]都是字符,没有‘*’(可以有‘.’),我们可以简单的判断其是否可匹配

  1. 如果二者不等,直接返回false
  2. 如果二者相等,递归到s,i-1,p,j-1的情况即可

对于p[j]的字符是‘*’的情况,首先‘*’都是和其前面一个字符组合起作用的,具体执行思路如下

  1. 判断这一个组合能不能匹配到s中的字符,如果s[i]=p[j-1](包括‘.’),就可前移i(i--),在这过程中如果发现两个字符串匹配成功,即可返回true,如果发现两者不能匹配了,并不能说明两字符串不能匹配,可能是因为该组合左右已经发挥完毕,应该递归到,s,i,p,j-2

算法实现

//回溯法
class Solution {
public:
    bool isMatch(string s, string p) {
        return isMatch(s,s.length() - 1, p, p.length() - 1);
    }
    bool isMatch(string s, int i, string p, int j)
    {
        //如果p已经匹配完,而s还没有则错误,如果二者同时匹配完则正确
        if(j == -1)
        {
            if(i == -1)return true;
            else 
                return false;
        }
        if(s[i] == p[j] || p[j] == '.')return isMatch(s,i-1,p,j-1);
        if(p[j] == '*')
        {
            if((i > -1) && (s[i] == p[j - 1] || p[j-1] == '.'))
            {
                //这里要用j而不是j-2,因为#*可能匹配s的不止一个字符
                if(isMatch(s,i-1,p,j))return true;
            }
            return isMatch(s,i,p,j-2);
        }
        return false;
    }
};

2、DP(动态规划法)

算法描述

与回溯法相反,动态规划法是我偏要从前向后扫描计算能否匹配,其定义了一个(n+1)*(m+1)的bool型数组,根据已有的位置数据将整个数组填满


a),初始化数组的头,以方便后续数值传递

b[0][0]值为true,因为空字符串可以匹配空字符串

b[*][0]值为false,因为空字符串p不能匹配非空s

b[0][*]值要判断其是否前面均为#+*的组合,若是则真,否则为假


b),数值传递

(i),对于p当前字符不为*的字符,非常简单,b[i][j] = b[i - 1][j - 1] && (p[j - 1] == '.' || p[j - 1] == s[i - 1]),即值由两字符串当前位置的字符值是否相等和两字符串在当前位置之前的字符能否匹配决定

(ii),对于p当前字符为*的字符,则较为复杂,其值由以下两种情况综合决定,无论一下哪种情况满足,其值为真;若均不满足,则其值为假

            <1>*表示其前的字符重复了0次,即其值与b[i][j - 2]的值相关;

            <2>*表示其前的字符重复了1次或几次,则需判断p当前位置的前一个位置的字符与s当前位置的字符能否匹配,且其值更与b[i - 1][j]相关

算法实现

class Solution{
public: 
    bool isMatch(string s, string p) {
        int m = s.length();
        int n = p.length();
        bool b[m+1][n+1];
        //两个字符串都是空串的情况
        b[0][0] = true;
        //对于空字符串p来说,不能匹配任意非空字符串s
        for(int i = 1; i <= m; i ++)b[i][0] = false;
        //反之,对于空字符串s来说,有可能匹配非空字符串p,只要全为形如“#*”的组合即可
        for(int j = 1; j <= n; j ++)b[0][j] = j > 1 && (p[j - 1] == '*') && b[0][j-2];
        //编程时我在考虑整个二维数组的扫描顺序时有所犹豫,但是仔细推敲发现行行扫描和列列扫描并无影响
        for(int i = 1; i <= m; i ++)
        {
            for(int j = 1; j <= n; j ++)
            {
                if(p[j - 1] == '*') b[i][j] = b[i][j - 2] || ((p[j - 2] == s[i - 1] || p[j - 2] == '.') && b[i - 1][j]);
                else b[i][j] = b[i - 1][j - 1] && (p[j - 1] == '.' || p[j - 1] == s[i - 1]);
            }
        }
        return b[m][n];
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值