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
DP Code
class Solution
{
public:
/* dp[i][j]:
* true -> s[0...i-1] matches p[0...j-1]
* flase -> otherwise
*
* (1) trivial string
* p[i][j] = p[i-1][j-1], if p[j-1] != '*' && (s[i-1] == p[j-1] || p[j-1] == '.')
* (2) x* matches empty string
* p[i][j] = p[i][j-2], if p[j-1] == '*'
* (3) x* -> xx*, i.e. x repeats for at least 1 times
* p[i][j] = p[i-1][j] && (s[i-1] == p[j-2] || p[j-2] == '.'), if p[j-1] == '*'
*/
bool isMatch(string s, string p)
{
int m = s.length(), n = p.length();
vector<vector<bool> > dp(m+1, vector<bool>(n+1, false));
dp[0][0] = true; // initialization
for (int i = 0; i <= m; i++)
for (int j = 1; j <= n; j++)
if (p[j-1] == '*')
dp[i][j] = dp[i][j-2] || i > 0 && dp[i-1][j] && (s[i-1] == p[j-2] || p[j-2] == '.');
else
dp[i][j] = i > 0 && dp[i-1][j-1] && (s[i-1] == p[j-1] || p[j-1] == '.');
return dp[m][n];
}
};
Runtime:
20 ms
Backtrack Code
struct backtrackPoint
{
int stringIndex, patternIndex;
backtrackPoint() { stringIndex = patternIndex = 0; }
backtrackPoint(int stringIndex, int patternIndex): stringIndex(stringIndex), patternIndex(patternIndex){ }
};
struct pattern
{
string s;
pattern(const string& s): s(s) { c = '\0'; }
char c;
pattern(char c): c(c) { s = ""; }
pattern() { c = '\0'; s = ""; }
};
class Solution
{
public:
bool isMatch(string s, string p)
{
// get patterns
vector<pattern> patterns;
int start = 0, end = 0;
for (int i = 0; i < p.length(); i++)
{
if (p[i] == '.')
{
end = i - 1;
if (end >= start)
patterns.push_back(pattern(p.substr(start, end - start + 1)));
if (i+1 < p.length() && p[i+1] == '*')
{
patterns.push_back(pattern('.'));
i++;
}
else
patterns.push_back(pattern("."));
start = i + 1;
}
else if (p[i] == '*')
{
end = i - 2;
if (end >= start)
patterns.push_back(pattern(p.substr(start, end - start + 1)));
patterns.push_back(pattern(p[i-1]));
start = i + 1;
}
}
if (p[p.length() - 1] != '*')
patterns.push_back(pattern(p.substr(start)));
// print patterns
/*
for (int i = 0; i < patterns.size(); i++)
{
cout << "Patterns[" << i << "]: ";
if (patterns[i].s.length() > 0)
cout << patterns[i].s << " ";
else
cout << patterns[i].c << "* ";
cout << endl;
}
*/
int patternIndex = 0;
int m = 0;
stack<backtrackPoint> backtrackPoints;
bool backtrack = false;
while (true)
{
//cout << "stringIndex: " << m << " patternIndex: " << patternIndex << " backtrackPoints.size(): " << backtrackPoints.size() << endl;
if (patternIndex == patterns.size()) // pass all sub pattern(s), check
{
if (m == s.length())
return true;
if (m < s.length()) // the whole string matches preceding sub pattern(s) of the whole pattern, backtrack
{
if (backtrackPoints.size() > 0)
{
backtrackPoint point = backtrackPoints.top();
backtrackPoints.pop();
m = point.stringIndex;
patternIndex = point.patternIndex;
backtrack = true;
continue;
}
else
return false;
}
}
pattern currentPattern = patterns[patternIndex];
if (m == s.length())
{
bool flag = true;
for (int idx = patternIndex; idx < patterns.size(); idx++)
if (patterns[idx].s.length() > 0)
{
flag = false;
break;
}
if (flag) // current and latter sub pattern(s) are all star expression(s)
return true;
else // backtrack
{
if (backtrackPoints.size() > 0)
{
backtrackPoint point = backtrackPoints.top();
backtrackPoints.pop();
m = point.stringIndex;
patternIndex = point.patternIndex;
backtrack = true;
continue;
}
else
return false;
}
}
if (currentPattern.s.length() > 0) // trivial string
{
if (currentPattern.s.compare(".") == 0 || s.compare(m, currentPattern.s.length(), currentPattern.s) == 0)
{
m += currentPattern.s.length();
patternIndex++;
}
else // backtrack
{
if (backtrackPoints.size() > 0)
{
backtrackPoint point = backtrackPoints.top();
backtrackPoints.pop();
m = point.stringIndex;
patternIndex = point.patternIndex;
backtrack = true;
continue;
}
else
return false;
}
}
else // star expression
{
if (backtrack == true) // eat a character
{
backtrack = false;
if (currentPattern.c == '.' || s[m] == currentPattern.c) // eat a character
{
m++;
backtrackPoints.push(backtrackPoint(m, patternIndex));
patternIndex++;
}
else // backtrack
{
if (backtrackPoints.size() > 0)
{
backtrackPoint point = backtrackPoints.top();
backtrackPoints.pop();
m = point.stringIndex;
patternIndex = point.patternIndex;
backtrack = true;
continue;
}
else
return false;
}
}
else // eat no character, i.e. pass current star expression
{
backtrackPoints.push(backtrackPoint(m, patternIndex));
patternIndex++;
}
}
}
cout << "Error: undefined!" << endl;
}
};
Runtime:
368 ms
Recursive Code
class Solution
{
public:
bool isMatch(string s, string p)
{
if (p.empty())
return s.empty();
if (p[1] == '*')
// x* matches empty string or at least one character: x* -> xx*
return isMatch(s, p.substr(2)) ||
!s.empty() && (s[0] == p[0] || '.' == p[0]) && isMatch(s.substr(1), p);
else
// trivial string
return !s.empty() && (s[0] == p[0] || '.' == p[0]) && isMatch(s.substr(1), p.substr(1));
}
};
Runtime:
624 ms