刷LeetCode(10)——Regular Expression Matching
Code it now!https://leetcode.com/problems/regular-expression-matching/description/
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
解法:动态规划
动态规划解题思路大致如下:
动态规划所处理的问题是一个多阶段决策问题,一般由初始状态开始,通过对中间阶段决策的选择,达到结束状态。这些决策形成了一个决策序列,同时确定了完成整个过程的一条活动路线(通常是求最优的活动路线)。如下所示。动态规划的设计都有着一定的模式,一般要经历以下几个步骤。
初始状态→│决策1│→│决策2│→…→│决策n│→结束状态
可总结归纳成如下4个步骤:
A.划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。
B.确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。
C.确定决策并写出状态转移方程:因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。但事实上常常是反过来做,根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程。
D.寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。
一般,只要解决问题的阶段、状态和状态转移决策确定了,就可以写出状态转移方程(包括边界条件)。
然后我们依据题目来进行分析:
(1)划分阶段:
(2)确定状态:我们使用f[i][j]来代表s[0…i-1]和p[0…j-1]是否match
(3)边界条件:
A. s和p两个数组都为空:
f[0][0] = true;
B. s数组不为空,p数组为空:
f[i][0] = false;
C. s数组为空,p数组不为空:
if( j > 1 && '*' == p[j - 1] && f[0][j - 2] )
f[0][j] = true;
D.s数组不为空,p数组不为空:
这个时候又要根据p[j-1]是否为’*’来进行分情况讨论:
1. p[j-1] != ‘*’ :
if( f[i - 1][j - 1] && (s[i - 1] == p[j - 1] || '.' == p[j - 1]) )
f[i][j] = true;
2. p[j-1] == ‘*’ :
这个时候又需要根据’*’是匹配的特征来进行分情况讨论:
a. “x*” repeats 0 time and matches empty:
if( j>1 && f[i][j - 2] )
f[i][j] = true;
b. “x*” repeats >= 1 times and matches “x*x”:
if( (s[i - 1] == p[j - 2] || '.' == p[j - 2]) && f[i - 1][j] )
f[i][j] = true;
有了边界条件和状态转移方程,实现就变得十分简单了,代码如下:
#include <iostream>
#include <string>
#include <vector>
#include <assert.h>
using namespace std;
class Solution {
public:
bool isMatch(string s, string p) {
/**
* 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
*/
int m = s.size(), n = p.size();
vector< vector<bool> > f(m + 1, vector<bool>(n + 1, false));
// A . if s and p are empty .
f[0][0] = true;
// B. if s is not empty , but p is empty .
for (int i = 1; i <= m; i++){
f[i][0] = false;
}
// C. if s is empty , but p is not empty .
// p[0.., j - 3, j - 2, j - 1] matches empty iff p[j - 1] is '*' and p[0..j - 3] matches empty
for (int j = 1; j <= n; j++){
f[0][j] = j > 1 && '*' == p[j - 1] && f[0][j - 2];
}
// s, p are not empty .
for (int i = 1; i <= m; i++){
for (int j = 1; j <= n; j++){
if (p[j - 1] != '*'){
f[i][j] = f[i - 1][j - 1] && (s[i - 1] == p[j - 1] || '.' == p[j - 1]);
}
else{
// p[0] cannot be '*' so no need to check "j > 1" here
f[i][j] = f[i][j - 2] || (s[i - 1] == p[j - 2] || '.' == p[j - 2]) && f[i - 1][j];
}
}
}
return f[m][n];
}
};
string stringToString(string input) {
assert(input.length() >= 2);
string result;
for (int i = 1; i < input.length() -1; i++) {
char currentChar = input[i];
if (input[i] == '\\') {
char nextChar = input[i+1];
switch (nextChar) {
case '\"': result.push_back('\"'); break;
case '/' : result.push_back('/'); break;
case '\\': result.push_back('\\'); break;
case 'b' : result.push_back('\b'); break;
case 'f' : result.push_back('\f'); break;
case 'r' : result.push_back('\r'); break;
case 'n' : result.push_back('\n'); break;
case 't' : result.push_back('\t'); break;
default: break;
}
i++;
} else {
result.push_back(currentChar);
}
}
return result;
}
string boolToString(bool input) {
return input ? "True" : "False";
}
int main() {
string line;
while (getline(cin, line)) {
string s = stringToString(line);
getline(cin, line);
string p = stringToString(line);
bool ret = Solution().isMatch(s, p);
string out = boolToString(ret);
cout << out << endl;
}
return 0;
}