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
解題思路
時間復(fù)雜度: O(nm)
字符串比較一般都可以先想到動態(tài)規(guī)劃哮肚,此題只需要計算一個n*m
的矩陣即可求解卤档,所以復(fù)雜度是O(nm)
。(n
和m
分別是p
和s
的長度)
1姨拥、n*m
的矩陣用于記錄正則式p
從開頭到下標(biāo)i結(jié)束的子串與目標(biāo)字符串s
從開頭到下標(biāo)i結(jié)束的子串的比較結(jié)果鱼炒。
2叶撒、基于下標(biāo)小的元素以及比對字符可以計算出下標(biāo)大的元素,從而填滿整個矩陣贪嫂。
3寺庄、矩陣計算完畢后,返回對應(yīng)元素即可力崇。
4斗塘、矩陣的元素是怎么計算的,用一個例子來說明亮靴,如下圖:
圖1. 示例
- 橫向是目標(biāo)字符串
s
馍盟,縱向是正則式p
。定義該矩陣為C
- 初始化第一行茧吊。當(dāng)
s
和p
均為空時贞岭,匹配正確八毯,所以C[0][0] = 1
。當(dāng)p
為空串而s
不為空時瞄桨,一定匹配錯誤话速,所以第一行其他列均賦值為0。 - 初始化第一列芯侥。第一列表示s為空串泊交,這種情況只有當(dāng)p中出現(xiàn)*時匹配結(jié)果可能為真,如圖中第一列標(biāo)紅的位置柱查。當(dāng)p中出現(xiàn)
*
時廓俭,該元素同一列上前兩個元素任意一個為真,它就為真唉工。具體原因在4.中解釋研乒,所以C[2][0] = C[0][0]
。 - 計算其他元素淋硝。有如下情況:
p[i] == p[j] || p[i] == '.': // 該元素對應(yīng)子串匹配正確
C[i + 1][j + 1] = C[i][j]
p[i] == '*': // x*代表字符x出現(xiàn) 0 次雹熬、1 次、1次以上奖地。
C[i + 1][j + 1] = C[i - 1][j + 1] // x*代表x出現(xiàn)0次時橄唬,匹配結(jié)果等同于“x*”沒出現(xiàn)過
//即當(dāng)前 p 的子串向前跳兩個字符與當(dāng)前 s 的子串的匹配結(jié)果
C[i + 1][j + 1] = C[i][j + 1] // x*代表x出現(xiàn)1次時赋焕,匹配結(jié)果等同于沒有該星號参歹,
// 以前一個字符為結(jié)尾的 p 的子串與 s 的子串的匹配結(jié)果。
C[i + 1][j + 1] = C[i + 1][j] // x*代表x出現(xiàn)1次以上時隆判,只要當(dāng)前 s 的子串的結(jié)尾字
// 符與 p 的子串的 * 的前一個字符(別忘了'.'的情況)能匹配上犬庇,
// 當(dāng)前匹配結(jié)果就等同于以 * 前一位字符結(jié)尾的 p 子串與 s 子串的匹配結(jié)果。
簡單來說侨嘀,p[i] == '*'
的情況只需要考慮如圖中的反向“L”區(qū)域臭挽,其他位置出現(xiàn) 1 則當(dāng)前位置為 1 ,否則為 0 咬腕。
解題源碼
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.length(), n = p.length();
vector<vector<bool>> check(n + 1, vector<bool>(m + 1, false));// 用 false 初始化所有元素
// 初始化第一個元素欢峰,其他第一行元素已經(jīng)初始化為 false 了
check[0][0] = true;
// 初始化第一列
for (int i = 0; i <n; ++i) {
if (p[i] == '*') {
check[i + 1][0] = i >= 1 && check[i - 1][0] || i >= 0 && check[i][0];
}
}
// 計算其他元素
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (p[i] == '.' || p[i] == s[j]) check[i + 1][j + 1] = check[i][j];
else if (p[i] == '*') {
check[i + 1][j + 1] = check[i - 1][j + 1] || check[i][j + 1] || (p[i - 1] == s[j] || p[i - 1] == '.') && check[i + 1][j];
}
}
}
// 返回結(jié)果
return check[n][m];
}
};