zoukankan      html  css  js  c++  java
  • Leetcode OJ: Regular Expression Matching

    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).
    
    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
    

    实现个简单的正则匹配,支持'.'与'*'的操作

    解决这题的关键是思路要清晰,考虑要周全。

    1. 递归实现。

    思路:

    isMatch(s, p):

    1. 当前p为0时,若s也是0时,则返回true,否则为false

    2. 当前p不为0时,

      1) p的下一个不是'*'时

        if: 当前s与p匹配,

          则表明到此都是匹配的,只需要检查isMatch(s + 1, p + 1)

        else:

          返回false

      2) p的下一个是'*'时,

        while: 当前s与p匹配,即表明至此也是匹配的

          if: 当前s与下一个p也都匹配,即isMatch(s, p + 2),则返回true

          else: s++

        此时,当前s与当前p已经不匹配了(之前s都是匹配的),则检测下一个模板isMatch(s, p + 2)

    代码如下:

     1 class Solution {
     2 public: 
     3     bool isMatch(const char *s, const char *p) {
     4         if (*p == '') return *s == '';
     5         if (*(p+1) != '*') {
     6             return ((*p == *s) || (*p == '.' && *s != '')) && isMatch(s+1, p+1);
     7         }
     8         while ((*p == *s) || (*p == '.' && *s != '')) {
     9             if (isMatch(s, p+2)) return true;
    10             s++;
    11         }
    12         return isMatch(s, p+2);
    13     }
    14 };

    最终是116ms过了

    2. 动态规划实现

    其实根据以上思路,就很容易就有动态规划的思路了,小小的trick就是假设s与p的开头都加了一个空字符,方便统一规则。

    DP(i, j)存的是p[0:i-1]与s[0:j-1]的匹配情况。

    且看下面的图与说明

    DP(i+1, j+1)是要更新的状态,对应p[i]和s[j]

    1. 当p[i] 的下一个不为'*'时,此时只跟上一个状态的p与上一个s对应的DP(i, j)有关

     因为此时匹配的条件是:A. p[i]与s[j]匹配。B. p[0:i-1]与s[0:j-1]完全匹配,即DP(i, j)==true。

     则有 

        DP(i+1, j+1) = DP(i, j) && Match(p[i], s[j])

     另外考虑DP(i+1, 0),这个值对应的是s[-1]与p[i],s[-1]即假设存在的空字符,明显p[i]与s[-1]是不匹配的

       因此这时的DP(i+1, 0) = false

     而要让规划顺利地开始,(特别是当第一个匹配对的就是这种情况时)就需要让DP(0, 0)=true了。

    2. 当p[i]的下一个为'*'时,此时就与围绕这一状态的都有关。

      1) 如果DP(i, j+1)是true时,即上一个pattern就已经完全匹配了当前的s,那当前状态也应该是true,即DP(i+1, j+1)=true

      2) 否则,DP(i, j)与DP(i+1, j)其中有一个为true, 而且p[i]与s[j]匹配,则DP(i+1, j+1)为true,即

        DP(i+1, j+1) = (DP(i, j) || DP(i+1, j)) && Match(p[i], s[j])

     此时考虑DP(i+1, 0)则需要DP(i, 0)的配合,DP(i+1, 0) = DP(i, 0) && Match(p[i], s[-1])

     而Match(p[i], s[-1])永远为真,所以DP(i+1, 0) = DP(i, 0)

    另外考虑到匹配情况只跟相邻两层有关,所以实现时就只用了两层存状态。代码如下

     1 class Solution {
     2 public: 
     3     bool isMatchSingle(char s, char p) {
     4         return p == s || p == '.';
     5     }
     6     
     7     bool isMatch(const char *s, const char *p) {
     8         int slen = strlen(s);
     9         int plen = strlen(p);
    10         // 用于存当前层匹配结果与上一层匹配结果
    11         vector<bool> dp1(slen + 1, false), dp2(slen + 1, false);
    12         vector<bool> *pre = &dp1, *cur = &dp2;
    13         
    14         // 第一个为true,因为默认地给s与p的开始都添加了个空字符
    15         dp1[0] = true;
    16         const char* pp = p;
    17         while (pp[0] != 0) {
    18             // 指向当前层匹配结果与上一层匹配结果的引用
    19             vector<bool>& curr = *cur;
    20             vector<bool>& prer = *pre;
    21             
    22             // 初始化
    23             curr.assign(slen + 1, false);
    24             if (pp[1] != '*') {
    25                 // curr[0]都将为false
    26                 for (int i = 0; i < slen; ++i) {
    27                     curr[i + 1] = (prer[i] && isMatchSingle(s[i], pp[0]));
    28                 }
    29                 pp += 1;
    30             } else {
    31                 // curr[0]只与prer[0]相关
    32                 curr[0] = prer[0];
    33                 for (int i = 0; i < slen; ++i) {
    34                     curr[i + 1] = (prer[i + 1] || (isMatchSingle(s[i], pp[0]) && (prer[i] || curr[i])));
    35                 }
    36                 pp += 2;
    37             }
    38             // 交换,注意是指针的交换,没有换数据
    39             swap(cur, pre);
    40         }
    41         return (*pre)[slen];
    42     }
    43 };

    最终52ms过了。

    个人也加了些优化,给每一层都加个计数器,当是没有*的情况下,上一层为True的节点已经到结尾了,26行的循环就可以跳出了。

    实现了一记,24ms过了,有兴趣的同学可以试试看。

    这道题还是挺复杂了,理清思路很重要。
  • 相关阅读:
    HDU 1429
    HDU 1622
    HDU 3335
    HDU 4160
    HDU 1350
    HDU 5086
    HDU 1300
    HDU 3047
    HDU 3038
    HDU 5100
  • 原文地址:https://www.cnblogs.com/flowerkzj/p/3726667.html
Copyright © 2011-2022 走看看