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 letters a-z.
p could be empty and contains only lowercase letters a-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
code:
class Solution {
public:
bool isMatch(string s, string p) {
if(p.empty())
return s.empty();
else if('*'==p[1])
return !s.empty()&&(s[0]==p[0]||'.'==p[0])&&isMatch(s.substr(1), p)||isMatch(s, p.substr(2));
else
return !s.empty()&&(s[0]==p[0]||'.'==p[0])&&isMatch(s.substr(1), p.substr(1));
}
};
comment:
272 ms | 15 MB | Cpp |
It's recursion.
Solution 2: Dynamic Programming
code:
class Solution {
public:
bool isMatch(string s, string p)
{
if(p.empty()) return s.empty();
////first of all, all false, and dp[i][j] is true when p[j] matches s[i]
vector<vector<bool>> dp(s.size()+1, vector<bool>(p.size()+1, false));
dp[0][0] = true;//bcz '' match ''
if(s.size()!=0&&(s[0]==p[0]||p[0]=='.')) dp[1][1] = true;
if(p[0]=='*') dp[0][1] = true;
for(int j = 1; j<p.size(); j++)
{
if(p[j]=='*'&&dp[0][j-1]==true) dp[0][j+1] = true;
}
for(int i = 0; i<s.size(); i++)
{
for(int j = 1; j<p.size(); j++)
{
if(p[j]==s[i]||p[j]=='.') dp[i+1][j+1] = dp[i][j];
if(p[j]=='*')
{
if(p[j-1]!=s[i]&&p[j-1]!='.'){
dp[i+1][j+1] = dp[i+1][j-1];
}else{
dp[i+1][j+1] = dp[i][j+1]||dp[i+1][j-1];
}
}
}
}
return dp[s.size()][p.size()];
}
};
comment:
12 ms | 8.9 MB | Cpp |
I use s = "aaa", p = "ab*a*c*a" to demonstrate followings:
1) Don't mix up "if(p.empty()) return s.empty()" with "if(s.empty()) return p.empty();". Cuz there is test cases such s="",p=".*.*".
2)s[0] is the first char of s, p[0] is the first char of p, but dp[1][1] means the first match of s[0] and p[0]. Hence vector<vector<bool>> dp has strings' size plus one in each dimension.
3)First of all we have dp[0][0] = true, that is, an initialization of dp. You may imagine that dp[0][0] means a null char before s[0] matches a null char before p[0], that's true.
4)"if(s.size()!=0&&(s[0]==p[0]||p[0]=='.')) dp[1][1] = true;" Don't miss s.size()!=0 cuz there is test cases such s="", p=".";
5)"if(p[0]=='*') dp[0][1] = true;" Even if dp[0][1] = false is alright.
6)
for(int j = 1; j<p.size(); j++)
{
if(p[j]=='*'&&dp[0][j-1]==true) dp[0][j+1] = true;
}
Here we initialize for when s="", p=".*.*"
7)In dat main loop, when "(p[j]==s[i]||p[j]=='.')", if dp[i][j] matches, that's dp[i+1][j+1] matches.
8)When "(p[j]=='*')", "if(p[j-1]!=s[i]&&p[j-1]!='.')" means if the last p doesn't match current s and is not '.'. And "dp[i+1][j+1] = dp[i+1][j-1];" means if the numbers b4 last p matches numbers of current s(dp[i+1][j-1];). i+1 also matches j+1. For example, when i = 1, j = 6, now we have s = "aa", p = "ab*a*c*", c doesn't matches to a, but "aa" matches "ab*a*", hence if c* have 0 c, "aa" can also matches "ab*a*c*".
9)When "(p[j]=='*')", if the last p matches current s or is '.'. And "dp[i+1][j+1] = dp[i][j+1]||dp[i+1][j-1];" "dp[i][j+1]" for example when i = 1, j = 4, we have "aa" "ab*a*", d[i][j+1] is "a" matches to "ab*a*"(notice that d[i][j+1] is s[0] with p[j]), so d[i+1][j+1] that is "aa" also matches to "ab*a*". dp[i+1][j-1] for example when i = 0, j = 4 , we have "a" "ab*a*", d[i+1][j-1] is "a" matches to "ab*", so d[i+1][j+1] that is "a" also matches to "ab*a*".
10)I highly recommend s="aaa" p="ab*a*c*a" as the test case like above to go through almost all processes of the function.