每日一題:正則表達式匹配

給你一個字符串 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’ 重複了一次。

示例3:
輸入:
s = “ab”
p = “.*”
輸出: true
解釋: “.*” 表示可匹配零個或多個(’*’)任意字符(’.’)。

示例4:
輸入:
s = “aab”
p = “c*a*b”
輸出: true
解釋: 因爲 ‘*’ 表示零個或多個,這裏 ‘c’ 爲 0 個, ‘a’ 被重複一次。因此可以匹配字符串 “aab”。

示例5:
輸入:
s = “mississippi”
p = “mis*is*p*.”
輸出: false

遞歸法

最直觀的是採用遞歸的方式,依此匹配過去。模版字符串爲p,待匹配的字符串爲s。若s[i] == p[j],則繼續遞歸匹配s[i+1:]和p[j+1:]。若p[j]==’*’,且p[j-1]==s[i],則繼續遞歸匹配p[j:]和s[i+1:],否則繼續遞歸匹配p[j+2:]和s[i:]。因爲字符加星號的組合可以匹配s串裏任意個字符,所以我們要遞歸計算所有可能的情況,這也是“return (isMatch(s, p.substr(2)) || (firstMatch && isMatch(s.substr(1), p)));”要做一個或運算的原因,就是爲了考慮到字符加星號匹配任意個字符的情況,否則就變成了貪心地匹配最多的字符。代碼如下:

class Solution {
public:
    bool isMatch(string s, string p) {
        if (p.length() == 0) return s.length() == 0;
        bool firstMatch = (s.length() != 0 && (p[0] == s[0] || p[0] == '.' ));

        if (p.length() >= 2 && p[1] == '*') {
            // 避免出現匹配aaa和a*a這種情況,這中情況下由於a*組合的後面還有一個a,所以要提前丟棄a*組合;
            return (isMatch(s, p.substr(2)) || (firstMatch && isMatch(s.substr(1), p)));
        }
        else {
            return firstMatch && isMatch(s.substr(1), p.substr(1));
        }
    }
};

動態規劃

遞歸方法中isMatch函數回進行大量的重複計算,所以考慮將它改造成動態規劃。設一個二維動態規劃的數組dp[][],數據變量爲布爾值。dp[i][j]表示s中的前i個字符和前j個字符是否能匹配。
在狀態轉移時,我們考慮p的第j個字符的匹配情況。

  • 如果p的第j個字符是一個字母,則必須在s中也匹配一個同樣的字母,所以,如果有p[j] ==s[i],則有dp[i][j] =dp[i-1][j-1] ,否則,則有dp[i][j] = false;
  • 如果p的第j個字符是*,則要得到了一個字符加星號組,因爲一個字符加星號組可以匹配s中的任意的字符(可以爲0),所以必須充分地考慮每種情況。如果p[j-1]==s[i],則dp[i][j] = dp[i][j-2] || dp[i-1][j](因爲字符加星的組合可以匹配p中任意個數的字符,所以必須在每一步都考慮丟棄掉字符加星組合的情況,即表示爲dp[i][j-2]),如果p[j-1]!=\s[i],則dp[i][j] = dp[i][j-2]

因此,根據這個轉移方程來設計動態規劃的代碼:

class Solution {
public:
    bool isMatch(string s, string p) {
        int sLen = s.size(), pLen = p.size();
        if (pLen == 0) return sLen == 0;
        vector<vector<bool> > dpTable(sLen + 1, vector<bool>(pLen + 1, false));
        dpTable[0][0] = true;
        for (int i = 0; i <= sLen; ++i) {
            for (int j = 1; j <= pLen; ++j) {
                if (p[j - 1] == '*') {
                    dpTable[i][j] = dpTable[i][j] || dpTable[i][j - 2];
                    if (i > 0 && (p[j - 2] == '.' || p[j - 2] == s[i - 1])) {
                        dpTable[i][j] = dpTable[i][j] || dpTable[i - 1][j];
                    }
                }
                else {
                    if (i > 0 && (p[j - 1] == '.' || p[j - 1] == s[i - 1])) {
                        dpTable[i][j] = dpTable[i - 1][j - 1];
                    }
                }
            }
        }
        return dpTable[sLen][pLen];
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章