LeetCode系列之【10. 正则表达式匹配】C++ 每天一道leetcode!

目录(快速导航)

题目描述:

视频讲解 https://www.bilibili.com/video/av66324687/

1.递归回溯

2.动态规划



题目描述:

题目链接:https://leetcode-cn.com/problems/regular-expression-matching/

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。

'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

说明:s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。

示例 1:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

 

视频讲解

https://www.bilibili.com/video/av66324687/


两种办法解决

1.递归回溯

这种办法比较好理解。

我们使用递归回溯的方法,其实就是把所有可能的情况全部试一遍。

如:s=‘aaaa’,p='a*a',如果单纯让a*继续下去,那么p中a*就可以把s全匹配完,导致的结果是,算法以为p的字符多了个a,最终s和p没匹配上。当然全部的情况也包括:a*匹配aaa,让p的最后一个a和s的最后一个a匹配,这时的结果就是匹配上了。所以递归回溯把所有情况都试一遍,有一种成功匹配那就成功了。

本题目我们实现函数isMatch(string s,string p):通过不停的剪去s和p相同的首部,直到某一个或两个都被剪空,就可以得到结论(当然需要结合所有剪空的情况)。

首先我们从没有‘*’的最简单情况来看,这时是不是只需要扫一遍s和p,从首部开始比较对应的元素是否相同即可,如果相同就可以剪去,比较下一个即:

// 第i个下标位置元素的比较 '.'代表任何元素
s[i] == p[i] || p[i] == '.'

那么现在添加‘*’,需要注意题目要求,此时‘*’前的元素可以出现0次或者多次,那么注意在检测到p中第i个元素的下一个元素为‘*’时,就会有两种情况:

  1. p的第i个元素在s中出现0次;此时,我们保持s不变,将p剪2个元素,继续调用isMatch。如:s=‘bb’,p='a*bb',将p的首部2个元素剪去,得到p=‘bb’,继续比较;
  2. p的第i个元在s中出现一次或者多次;此时,比较i元素与s的首元素,如果相同,剪去s的首元素,保持p不变继续调用isMatch。如:s=‘aabb’,p='a*bb',那么比较首元素相同,再剪去s首元素得s=‘abb’,在调用isMatch比较。

当然会出现这种情况:s='abb',p='a*abb';按1来说,p剪去两个元素,s='abb',p='abb',成功;按2来说,s='bb',p='a*abb',失败;

所以,这会把所有情况全试一遍,就得到结果了。

代码:

class Solution {
public:
    bool isMatch(string s, string p) {
        // 在p为空后 只需要查看s是否为空即可
        if (p.empty()) {
            return s.empty();
        }
        // 查看首元素是否一致
        bool first_match = !s.empty() && (s[0] == p[0] || p[0] == '.');
        // 如果下一个字符是'*'
        if (p.size() >= 2 && p[1] == '*') {
            return (bool)( isMatch(s, p.substr(2)) || (first_match && isMatch(s.substr(1), p)));
        }
       // 一般情况
        else {
            return bool( first_match && isMatch(s.substr(1), p.substr(1)));
        }
    }
};

2.动态规划

动态规划的思想也很简单,就是找到最优子结构即可。我们使用:

dp(i,j)表示:s的前i个字符和p的前j个字符是否匹配。

接下来就是把dp这个矩阵给填满,从初始dp[0][0]=true开始,到得到dp[s.size()][p.szie()]为止。

LeetCode系列之【10. 正则表达式匹配】C++ 每天一道leetcode!_第1张图片

代码:

class Solution {
public:
    bool first_match(string s, string p, int i, int j) {
        return s[i] == p[j] || p[j] == '.';
    }

    bool isMatch(string s, string p) {
        vector> dp(s.size() + 1, vector(p.size() + 1));
        dp[0][0] = true;

        for (int j = 2;j <= p.size();j++) {
            dp[0][j] = p[j-1] == '*' && dp[0][j - 2];
        }
        for (int i = 0;i < s.size();i++) {
            for (int j = 0;j < p.size();j++) {

                if (p[j] == '*') {
                    dp[i + 1][j + 1] = dp[i + 1][j-1] || first_match(s,p,i,j-1) && dp[i][j +1];
                }
                else {
                    dp[i + 1][j + 1] = first_match(s,p,i,j) && dp[i][j];
                }
            }
        }
        return dp[s.size()][p.size()];
    }
};

一起加油!!刷题!!

你可能感兴趣的:(leetcode)