Regular Expression Matching

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

Difficulty:Hard

TIME:TIMEOUT

解法一(深度優先搜索)

這道題主要是要理解匹配的過程,就是要把匹配的過程分成幾種情況分別討論,最簡單的做法當然是用遞歸來求解,以下就對遞歸過程做一個描述。

首先是有*號的情況,也就是匹配0次或者1次以上:

  • 如果匹配0次,則isMatch(s,p) = isMatch(s,p.substr(2))
  • 如果匹配一次以上,則isMatch(s,p) = isMatch(s.substr(1),p)

然後是沒有*號的情況,也就是隻匹配一次:

  • 則isMatch(s,p) = isMatch(s.substr(1),p.substr(1))
void dfs(string s, int ss, string p, int pp, bool &result) {
    if (pp >= p.size() && ss >= s.size())
        result = true;
    if (result || pp >= p.size())
        return;
    if (pp + 1 < p.size() && p[pp + 1] == '*') { //有*的情況
        dfs(s, ss, p, pp + 2, result); //必定可以跳過
        if ((p[pp] == '.' || p[pp] == s[ss]) && ss + 1 <= s.size())
            dfs(s, ss + 1, p, pp, result); //但不一定能匹配一次以上
    }
    else if ((p[pp] == '.' || p[pp] == s[ss]) && ss + 1 <= s.size())
        dfs(s, ss + 1, p, pp + 1, result); //沒有*的情況
}
bool isMatch(string s, string p) {
    bool result = false;
    dfs(s, 0, p, 0, result);
    return result;
}

代碼的時間複雜度應該約爲O(2n)

解法二(動態規劃)

一開始壓根沒有想到這道題能用動態規劃來解,但確實可以這樣,因爲這道確實存在着最優子結構,而且和之前做過的Distinct Subsequences十分相似。

首先使用一個二維數組dp,第一維表示字符串s的長度,而第二維表示字符串p的長度(注意是長度而不是下標),值爲true或false,也就是p的前綴能否匹配s的前綴。

爲什麼要採用長度而不是下標呢,首先一點處理空串會簡單很多(如果採用下標,空串要單獨處理),第二點是可以避免很多的邊界條件判斷(之前要處理下標是-1的狀態,現在只需要處理下標是0的狀態就行了)。因此代碼會簡潔不是一點,Distinct Subsequences的處理方式也是同樣的道理 。

首先處理空串的情況,很容易理解dp[0][0]爲true,然後就是dp[0][j]的情況,也容易想到可以一直匹配成功直到出現第一個沒有帶*號的字符。而具體的最優子結構我直接用LeetCode上面的其他人寫的作爲參照,將會在代碼中給出:

/**
 * f[i][j]: if s[0..i-1] matches p[0..j-1]
 * if p[j - 1] != '*'
 *      f[i][j] = f[i - 1][j - 1] && s[i - 1] == p[j - 1]
 * if p[j - 1] == '*', denote p[j - 2] with x
 *      f[i][j] is true iff any of the following is true
 *      1) "x*" repeats 0 time and matches empty: f[i][j - 2]
 *      2) "x*" repeats >= 1 times and matches "x*x": s[i - 1] == x && f[i - 1][j]
 * '.' matches any single character
 */
bool isMatch(string s, string p) {
    vector<vector<bool>> dp(s.size() + 1, vector<bool>(p.size() + 1, false));
    dp[0][0] = true;
    for (int i = 1; i <= p.size(); i++) { //先處理空串的情況
        if (p[i - 1] == '*' && dp[0][i - 2])
            dp[0][i] = true;
    }
    for (int i = 1; i <= s.size(); i++) {
        for (int j = 1; j <= p.size(); j++) {
            if (j < p.size() && p[j] == '*') {
                if(dp[i][j - 1] || ((s[i - 1] == p[j - 1] || p[j - 1] == '.') && dp[i - 1][j + 1]))
                    dp[i][j + 1] = true;
            }
            else if (dp[i - 1][j - 1] && (s[i - 1] == p[j - 1] || p[j - 1] == '.')) {
                dp[i][j] = true;
            }
        }
    }
    return dp[s.size()][p.size()];
}

不得不說這個最優子結構是十分精妙的,很少的判斷,很少的步驟,就能涵蓋所有的情況。

代碼的時間複雜度爲O(n2)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章