一. 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;
}
代碼的時間複雜度應該約爲
解法二(動態規劃)
一開始壓根沒有想到這道題能用動態規劃來解,但確實可以這樣,因爲這道確實存在着最優子結構,而且和之前做過的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()];
}
不得不說這個最優子結構是十分精妙的,很少的判斷,很少的步驟,就能涵蓋所有的情況。
代碼的時間複雜度爲