給你一個字符串 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];
}
};