这题有三个解法:
- DFS 深搜 + memorization
- DP
- Greedy
另外,是否可能也可以用BFS做呢?
网上的这个链接不错:
https://blog.csdn.net/wrm_nancy/article/details/51296416
解法1:
注意:
- s或p为空时不能返回false,因为有可能s=“”, p=“*”,此时应返回true。
- memorization 包括visited和memo这两个字段。为什么需要memo字段呢? 这是因为’*‘也可以表示空字符。假设s=“aab”, p=“a?*b”:
vm[0][0].memo = true
vm[1][1].memo = true
vm[2][2].memo = ? needs vm[2][3]
vm[2][3].memo = true,因为都是’b’
我们可以看到vm[2][2]提前用到了vm[2][3],这样,当后来计算vm[2][3]时就可以以直接返回结果了。
class visitedMemo {
public:
bool visited;
bool memo;
visitedMemo(bool v = false, bool m = false) : visited(v), memo(m) {}
};
class Solution {
public:
/**
* @param s: A string
* @param p: A string includes "?" and "*"
* @return: is Match?
*/
bool isMatch(string &s, string &p) {
//if (s.empty() || p.empty()) return false;
//define vm[s.size()][p.size()]
vector<vector<visitedMemo>> vm;
vm.resize(s.size(), vector<visitedMemo>(p.size()));
return helper(s, 0, p, 0, vm);
}
bool helper(string const &s, int sIndex, string const &p, int pIndex, vector<vector<visitedMemo>> &vm) {
// if p reachees the end (pos is the last char + 1) first, s should also reaches the end
if (pIndex == p.size()) {
return (sIndex == s.size());
}
// if s reaches the end first, then p[pIndex..end] should be all *,
// for example, s = "ab", p = "a****"
if (sIndex == s.size()) {
for (int i = pIndex; i < p.size(); ++i) {
if (p[i] != '*') return false;
}
return true;
}
if (vm[sIndex][pIndex].visited) return vm[sIndex][pIndex].memo;
vm[sIndex][pIndex].visited = true;
if (p[pIndex] == '*') {
vm[sIndex][pIndex].memo = helper(s, sIndex + 1, p, pIndex, vm) ||
helper(s, sIndex, p, pIndex + 1, vm);
} else {
if ((s[sIndex] == p[pIndex]) || (p[pIndex] == '?'))
vm[sIndex][pIndex].memo = helper(s, sIndex + 1, p, pIndex + 1, vm);
else
vm[sIndex][pIndex].memo = false;
}
return vm[sIndex][pIndex].memo;
}
};
解法2: DP
注意:
- s和p都先加一个’a’,这样不影响结果正确性,而且对s="b"和p="b*"这样的情况比较好处理。因为如果不加’a’的话,dp[1][1]=true后,i = 1, j = 2时,因为dp[i-1][j-1]还是false,所以最终结果dp[1][2]还是false。
但是加了’a’后,s=“ba”, p=“b*a”, dp[1][1]=true后,i=2,j=2时,bp[1][2]和bp[2][2]都为true (实际上到这里就够了)。然后i=2,b=3时,bp[2][3]会用到bp[1][2]的结果,也为true. - 下面这个DP的方法讲得非常好。不过我的解法是i循环管s,j循环管p。他的解法是i循环管p, j循环管s。因为实际上s比p要多跑一些字符,因为p有*和?。
https://www.lintcode.com/problem/192/solution/56425
class Solution {
public:
bool isMatch(string s, string p) {
s = s + 'a';
p = p + 'a';
int m = s.size();
int n = p.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1));
dp[0][0] = true;
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (dp[i][j]) continue;
if (p[j - 1] == '*') {
if (dp[i - 1][j - 1]) {
for (int k = i - 1; k <= m; k++) dp[k][j] = true;
}
}
else if (p[j - 1] == '?') {
dp[i][j] = dp[i - 1][j - 1];
}
else if (dp[i - 1][j - 1] && s[i - 1] == p[j - 1]) {
dp[i][j] = true;
}
}
}
return dp[m][n];
}
};
解法3:还是DP。只是第1维和第2维反过来。这样可以方便用Passthru。不需要再加一个循环。不过其实解法2再加一个循环也没有影响复杂度。
class Solution {
public:
bool isMatch(string s, string p) {
s = s + 'a';
p = p + 'a';
int m = s.size();
int n = p.size();
vector<vector<bool>> dp(n + 1, vector<bool>(m + 1, false));
dp[0][0] = 1;
for (int i = 1; i <= n; i++) {
bool passthru = false;
for (int j = 1; j <= m; j++) {
if (dp[i - 1][j - 1] && p[i - 1] == '*') {
passthru = true;
dp[i][j - 1] = true;
} else if (dp[i - 1][j - 1]) {
if (p[i - 1] == '?' || s[j - 1] == p[i - 1]) {
dp[i][j] = true;
}
}
if (passthru) {
dp[i][j] = true;
}
}
}
return dp[n][m];
}
};
解法4: Greedy
下次做。