# 10. Regular Expression Matching (Facebook Hard)

## 题目
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("abc", ".*") → true
isMatch("aab", "c*a*b") → true
```

## 思路
'.' 匹配任意单字符，但不能匹配空字符；'*' 可以匹配 0 或多个前面一个字符，匹配 0 个就是将前面的字符删掉，匹配多个代表对前面字符的重复。'*' 也可以匹配前面的 '.' 即 '.*' 可以代表一个 '.' 两个 '.' 或者多个 '.' 每一步匹配都会出现多种条件判别情况，我们用递归和动态规划来分别实现。

## 题解1: Recursion
定义一个 isMatch 递归函数进行匹配，两个变量：s 和 p。先对 p 做条件判断，在此基础上，对 s 做条件判断。

### 条件
1. p.length() == 0, s 为空返回 true，s 不空则返回 false。
2. p.length() == 1  
    (2-1) s 为空返回 false；  
    (2-2) s 不为空，p.charAt(0) == s.charAt(0) 或者 p.charAt(0) == '.'  
    若满足则返回 isMatch(s.substring(1), p.substring(1))，否则为 false。
3. P.length() >= 2  
    (3-1) s 不空，p.charAt(1) != '*'，且满足 s.charAt(0) == p.charAt(0) || p.charAt(0) == '.' 时，若满足则返回 isMatch(s.substring(1), p.substring(1))，否则为 false。
    (3-2) s 不空，P.length() >= 2 p.charAt(1) == '*'，且满足 s.charAt(0) == p.charAt(0) || p.charAt(0) == '.' 时，如果该字符只出现一次 if (isMatch(s, p.substring(2))) 返回 true，否则重复执行 s = s.substring(1)
    (3-3) p.charAt(1) == '*', 返回 isMatch(s, p.substring(2)). 由上述，我们把条件 2 的最后一种情况和条件 3 的第一种情况合并。

### Time
O((m+n)x2^(m+n/2)), m=s.length,n=p.length
### Space
O(m^2 + n^2)

### 代码如下：
```
class Solution {
    public boolean isMatch(String s, String p) {
        if (p.length()==0) {
            return s.length()==0;
        }
        if (p.length() == 1 || p.charAt(1) != '*') {
            if (s.isEmpty() || (p.charAt(0) != '.' && p.charAt(0) != s.charAt(0))){
                return false;
            } else {
                return isMatch(s.substring(1), p.substring(1));
            }
        }
        while (!s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.')){
            if (isMatch(s, p.substring(2))) {
                return true;
            }
            s = s.substring(1);
        }
        return isMatch(s, p.substring(2));
    }
}
```

## 题解2: DP

定义一个 boolean dp[i][j]，用来表示 s[0-i] 与 p[0-j] 是否匹配。dp[0][0] 表示两个空字符串是否匹配，初始化为 true。dp[i][j] 为 true 的条件如下：

### 条件
1. s.charAt(i)=p.charAt(j)
2. p.charAt(j)='.'
3. p.charAt(j)='*'  
   (3-1) if p.charAt(j-1)!=s.charAt(i)， * matches zero of the preceding element. 
   (3-2) if p.charAt(j-1)==s.charAt(i)，或者 p.charAt(j-1) '.' * Matches one or more of the preceding element

### 注意⚠
如右图所示，考虑到这种情况("aab", "cab")中，c* 应匹配为空字符串，接下来应尝试匹配 aab 和 a*b。这个子问题的解决应从对应空字符串的匹配开始，即 dp[0][2] 应设为 true。

### Time
O(mn),m=s.length,n=p.length
### Space
O(mn)

### 代码如下：
```
class Solution {
  public boolean isMatch(String s, String p) {
    if (s == null || p == null) {
        return false;
    }
    boolean[][]dp=new boolean[s.length()+1][p.length()+1];
    dp[0][0]= true;
    for(int i=0;i<p.length();i++){  
        if(p.charAt(i)=='*'&& dp[0][i-1])
            dp[0][i+1]=true;
    }
    for(int i=0;i<s.length();i++){  
        for(int j=0;j<p.length();j++ ){  
            if(p.charAt(j)=='.'){  
                dp[i+1][j+1]=dp[i][j];
            }
            if(p.charAt(j)==s.charAt(i)){  
                dp[i+1][j+1]=dp[i][j];
            }
            if (p.charAt(j) == '*') {
                if (p.charAt(j-1) != s.charAt(i) && p.charAt(j-1) != '.') {
                    dp[i+1][j+1] = dp[i+1][j-1];
                }
                else {
                    dp[i+1][j+1] = (dp[i+1][j] || dp[i][j+1] || dp[i+1][j-1]);
                }
            }
        }
    }
    return dp[s.length()][p.length()];
  }
}
```
