Given an input string (s
) and a pattern (p
), 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).
Note:
s
could be empty and contains only lowercase lettersa-z
.p
could be empty and contains only lowercase lettersa-z
, and characters like.
or*
.
Example 1:
Input: s = "aa" p = "a" Output: false Explanation: "a" does not match the entire string "aa".
Example 2:
Input: s = "aa" p = "a*" Output: true Explanation: '*' means zero or more of the preceding element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".
Example 3:
Input: s = "ab" p = ".*" Output: true Explanation: ".*" means "zero or more (*) of any character (.)".
Example 4:
Input: s = "aab" p = "c*a*b" Output: true Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches "aab".
Example 5:
Input: s = "mississippi" p = "mis*is*p*." Output: false
Related Topics:
String, Dynamic Programming, Backtracking
Similar Questions:
// OJ: https://leetcode.com/problems/regular-expression-matching/
// Author: github.com/lzl124631x
// Time: O(MN)
// Space: O(M)
class Solution {
private:
inline bool matchChar(string &s, int i, string &p, int j) {
return p[j] == '.' ? i < s.size() : s[i] == p[j];
}
bool isMatch(string s, int i, string p, int j) {
if (j == p.size()) return i == s.size();
if (j + 1 < p.size() && p[j + 1] == '*') {
bool ans = false;
while (!(ans = isMatch(s, i, p, j + 2))
&& matchChar(s, i, p, j)) ++i;
return ans;
} else {
return matchChar(s, i, p, j) && isMatch(s, i + 1, p, j + 1);
}
}
public:
bool isMatch(string s, string p) {
return isMatch(s, 0, p, 0);
}
};
// OJ: https://leetcode.com/problems/regular-expression-matching/
// Author: github.com/lzl124631x
// Time: O(MN)
// Space: O(N)
class Solution {
int M, N;
bool dfs(string &s, string &p, int i, int j) {
while (i < M && j < N) {
if (j + 1 < N && p[j + 1] == '*') {
if (dfs(s, p, i, j + 2)) return true;
while (i < M && (p[j] == '.' || s[i] == p[j])) {
if (dfs(s, p, ++i, j + 2)) return true;
}
return false;
} else {
if (p[j] != '.' && s[i] != p[j]) return false;
++i, ++j;
}
}
if (i == M) {
while (j + 1 < N && p[j + 1] == '*') j += 2;
}
return i == M && j == N;
}
public:
bool isMatch(string s, string p) {
M = s.size(), N = p.size();
return dfs(s, p, 0, 0);
}
};
// OJ: https://leetcode.com/problems/regular-expression-matching/
// Author: github.com/lzl124631x
// Time: O(MN)
// Space: O(MN)
class Solution {
int M, N;
vector<vector<int>> m;
int dfs(string &s, string &p, int i, int j) {
if (m[i][j] != 0) return m[i][j];
while (i < M && j < N) {
if (j + 1 < N && p[j + 1] == '*') {
do {
m[i][j] = dfs(s, p, i, j + 2);
if (m[i][j] == 1) return 1;
if (p[j] == '.' || s[i] == p[j]) ++i;
else return m[i][j] = -1;
} while (i < M);
} else {
if (p[j] != '.' && s[i] != p[j]) return m[i][j] = -1;
++i, ++j;
}
}
if (i == M) {
while (j + 1 < N && p[j + 1] == '*') j += 2;
}
return i == M && j == N ? 1 : -1;
}
public:
bool isMatch(string s, string p) {
M = s.size(), N = p.size();
m.assign(M + 1, vector<int>(N + 1));
return dfs(s, p, 0, 0) == 1;
}
};
When I was looking at the DP solution provided by LeetCode, I thought why not iterating from the beginning?
Let dp[i][j]
be whether s[0..(i-1)]
and p[0..(j-1)]
matches, where i
is in [0, M]
, j
is in [0, N]
, and M
and N
are the lengths of s
and p
respectively.
The result would be dp[M][N]
.
Trivial case dp[0][0] = true
.
We handle the *
pattern when visiting *
, so we skip p[j-1]
if p[j] == '*'
.
- If
p[j-1] == '*'
, we can try using this*
pattern to:- match 0 element, so
dp[i][j] = dp[i][j - 2]
. - match 1 element if
p[j-2]
ands[i-1]
matches, sodp[i][j] = dp[i-1][j-2]
- if the above case is true and
p[j-2]
ands[i-2]
matches, match 2 elements, sodp[i][j] = dp[i-2][j-2]
. - ...
- keep trying until we are unable to match.
- match 0 element, so
- Otherwise, if
p[j-1]
ands[i-1]
matches,dp[i][j] = dp[i-1][j-1]
.
// OJ: https://leetcode.com/problems/regular-expression-matching/
// Author: github.com/lzl124631x
// Time: O(MN)
// Space: O(MN)
class Solution {
public:
bool isMatch(string s, string p) {
int M = s.size(), N = p.size();
vector<vector<bool>> dp(M + 1, vector<bool>(N + 1));
dp[0][0] = true;
for (int i = 0; i <= M; ++i) {
for (int j = 1; j <= N; ++j) {
if (j < N && p[j] == '*') continue; // the next element is '*', skip the current one
if (p[j - 1] == '*') {
int k = i;
do {
if (dp[i][j] = dp[k][j - 2]) break;
if (k > 0 && (p[j - 2] == '.' || s[k - 1] == p[j - 2])) --k;
else break;
} while (k >= 0);
} else if (i - 1 >= 0 && (p[j - 1] == '.' || s[i - 1] == p[j - 1])) dp[i][j] = dp[i - 1][j - 1];
}
}
return dp[M][N];
}
};
Let dp[i][j]
be whether s[i..(M-1)]
and p[j..(N-1)]
matches, where M
and N
are the lengths of s
and p
respectively, and i
is in [0,M]
and j
is in [0,N]
.
The result would be dp[0][0]
.
Trivial case dp[M][N] = true
.
- If
p[j + 1] == '*'
, then we have two options:- ignore this
p[j]*
pattern, sodp[i][j] = dp[i][j + 2]
. - If
s[i]
matchesp[j]
, we can usedp[j]*
to covers[i]
, sodp[i][j] = dp[i + 1][j]
.
- ignore this
- Otherwise, if
s[i]
matchesp[j]
,dp[i][j] = dp[i + 1][j + 1]
- Otherwise,
dp[i][j] = false
.
// OJ: https://leetcode.com/problems/regular-expression-matching/
// Author: github.com/lzl124631x
// Time: O(MN)
// Space: O(MN)
// Ref: https://leetcode.com/problems/regular-expression-matching/solution/
class Solution {
public:
bool isMatch(string s, string p) {
int M = s.size(), N = p.size();
vector<vector<bool>> dp(M + 1, vector<bool>(N + 1));
dp[M][N] = true;
for (int i = M; i >= 0; --i) {
for (int j = N - 1; j >= 0; --j) {
bool match = i < M && (p[j] == '.' || p[j] == s[i]);
if (j + 1 < N && p[j + 1] == '*') dp[i][j] = dp[i][j + 2] || (match && dp[i + 1][j]);
else dp[i][j] = match && dp[i + 1][j + 1];
}
}
return dp[0][0];
}
};