LeetCode(10)RegularExpresssionMatching

本文深入探讨了正则表达式匹配算法的实现原理及难点,通过递归方法解决了含有‘*’的复杂匹配问题,并提供了简洁高效的代码示例。

我觉这道题目得挺难的,想了很久也没想清楚%>_<%于是看了解题报告

题目解释

'.' Matches any single character.
'*' Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
The function prototype should be:
bool isMatch(const char *s, const char *p)
Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true

给定的例子中,有一个比较难懂,是这样的。
isMatch("ab", ".*") → true
这个式子的判定结果是true。理由是.*理解为重复任意字符任意次.......而a是任意字符,b也是任意字符。所以ture


基本思路:
比较困难的问题在这里。看下面两个例子的比较:
例1: isMatch("aaaaab","a*b"),
为了表达方便记录为    isMatch("a1a2a3a4a5b1","a6*b2"),
显然a6应该匹配a1a2a3a4a5,然后b2匹配b1。

例2: isMatch("aaaaab","a*ab"),
为了表达方便记录为    isMatch("a1a2a3a4a5b1","a6*a7b2"),
显然a6应该匹配a1a2a3a4,然后a7匹配a5,然后b2匹配b1。
于是发现,当正则表达式中有*的时候,比较讨厌,你不知道到底要在s中匹配多长。
这个问题的结构说明这个问题天生适合用递归来解决。就像leetcode的解答报告中说的If you are stuck, recursion is your friend.
这个问题的结构还说明*很特殊所以需要特殊对待,于是基本思路是
把p分隔为若干个小单位,要么是一个字符构成的小单位,要么是一个字符和后面一个'*'构成的小单位。
逐一检查这样的小单位是否能够和s匹配。这其中,到底这样的小单位匹配多长的s是需要我们逐一检查的,也就是需要我们暴力一下的。

抄袭代码:

//
//  Solution.h
//  LeetCodeOJ_010_RegMatching
//
//  Created by feliciafay on 12/4/13.
//  Copyright (c) 2013 feliciafay. All rights reserved.
//

#ifndef LeetCodeOJ_010_RegMatching_Solution_h
#define LeetCodeOJ_010_RegMatching_Solution_h
#include <iostream>
#include <string>
#include <assert.h>
// Input:	"bbbba", ".*a*a"
class Solution {
public:
    bool isMatch(const char *s, const char *p) {
        assert(s && p);
        if (*p == '\0') return *s == '\0';
        
        // next char is not '*': must match current character
        if (*(p+1) != '*') {
            assert(*p != '*');
            return ((*p == *s) || (*p == '.' && *s != '\0')) && isMatch(s+1, p+1);
        }
        // next char is '*'
        while ((*p == *s) || (*p == '.' && *s != '\0')) {
            if (isMatch(s, p+2))
                return true;
            s++;
        }
        //继续匹配剩下的部分
        return isMatch(s, p+2);
        }
};
#endif

小结
(1)欣赏一下这句的简洁和巧妙 
return ((*p == *s) || (*p == '.' && *s != '\0')) && isMatch(s+1, p+1);
(2)
注意这里是
    if (isMatch(s, p+2)) return true;
而不是
    return  (isMatch(s, p+2));
因为逻辑是,在若干次暴力搜索中,只要又一次匹配就返回true,不管之前失败了几次。
(3)
之前个人错误地认为逻辑其实应该是
//错误写法
s++;
if (isMatch(s, p+2)) return true;
但是实际正确写法却是

if (isMatch(s, p+2)) return true;
s++;
为什么呢?

看这样一个例子就能理解了。 isMatch(“a”, "a*a"),这里"a"和"a*a"显然应该是匹配的。为了方便,表达为
原串: a1
正则: a2*a3
如果按照错误写法,那么a1匹配a2之后,将检查a3和'\0'发现不匹配于是返回结果为不匹配。
可是实际上,a2*表达的意思是把a2重复0次或者多次,错误的写法没有考虑到把a2重复0次的情况。
按照正确的写法,就可以考虑到a2重复0次的情况,从而判断a1是否匹配a3,从将最终结果返回为匹配。
所以正确的写法是

if (isMatch(s, p+2)) return true;
s++;
(4) LeetCode对思路讲解的的 原话

We need some kind of backtracking mechanism such that when a matching fails, we return to the last successful matching state and attempt to match more characters in s with ‘*’. This approach leads naturally to recursion.

If the next character of p is NOT ‘*’, then it must match the current character of s. Continue pattern matching with the next character of both s and p.
If the next character of p is ‘*’, then we do a brute force exhaustive matching of 0, 1, or more repeats of current character of p… Until we could not match any more characters.

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值