难题。一开始使用递归+备忘录的方式,我的递归写的太烂,大数据集合超时。要注意的是,一开始没考虑到"", "*"的case。超时的代码(即使后来加上长度的预先判断):
class Solution {
public:
int matrix[1000][1000];
bool isMatch(const char *s, const char *p) {
int slen = strlen(s);
int plen = strlen(p);
const char* tmp = p;
int cnt = 0;
while (*tmp != ' ') if (*(tmp++) != '*') cnt++;
if (cnt > slen) return false;
memset(matrix, 0, sizeof(matrix));
int r = isMatch(s, 0, slen, p, 0, plen);
return (r == 1);
}
private:
int isMatch(const char *s, int i, int slen, const char *p, int j, int plen)
{
if (matrix[i][j] != 0) return matrix[i][j];
if (i == slen && j == plen) return 1;
if (j == plen) return -1;
if (i == slen && p[j] == '*')
{
matrix[i][j] = isMatch(s, i, slen, p, j+1, plen);
return matrix[i][j];
}
if (p[j] == '?' || p[j] == s[i])
{
matrix[i][j] = isMatch(s, i+1, slen, p, j+1, plen);
return matrix[i][j];
}
if (p[j] == '*')
{
bool r = isMatch(s, i+1, slen, p, j, plen) == 1
|| isMatch(s, i, slen, p, j+1, plen) == 1;
matrix[i][j] = r ? 1:-1;
return matrix[i][j];
}
else
{
matrix[i][j] = -1;
return matrix[i][j];
}
}
};
参考里看到一个概念好的递归代码 http://discuss.leetcode.com/questions/222/wildcard-matching,虽然也大数据超时。精华部分是,如果遇到‘*’,那么s向右的任何一段子串和p跳过‘*’后的匹配成功都算成功:
class Solution {
public:
bool isMatch(const char *s, const char *p) {
if (*p == '*'){//return true;
while(*p == '*') ++p;
if (*p == ' ') return true;
while(*s != ' ' && !isMatch(s,p)){
++s;
}
return *s != ' ';
}
else if (*p == ' ' || *s == ' ') return *p == *s;
else if (*p == *s || *p == '?') return isMatch(++s,++p);
else return false;
}
};
但即使将上述方法转成备忘录模式,也超时。递归还是太多了。必须自底向上DP来做,总的来说,DP才是正道。这里有个很好的文章:http://www.iteye.com/topic/1131749 还有 http://blog.csdn.net/a83610312/article/details/9750655
下面是DP的代码:
class Solution {
public:
bool matrix[500][500];
bool isMatch(const char *s, const char *p) {
int slen = strlen(s);
int plen = strlen(p);
const char* tmp = p;
int cnt = 0;
while (*tmp != ' ') if (*(tmp++) != '*') cnt++;
if (cnt > slen) return false;
memset(matrix, 0, sizeof(matrix));
matrix[0][0] = true; // i,j means length
for (int i = 1; i <= plen; i++)
{
if (matrix[0][i-1] && p[i-1] == '*') matrix[0][i] = true;
for (int j = 1; j <= slen; j++)
{
if (p[i-1] == '*')
{
matrix[j][i] = (matrix[j-1][i] || matrix[j][i-1]);
}
else if (p[i-1] == '?' || p[i-1] == s[j-1])
{
matrix[j][i] = matrix[j-1][i-1];
}
else
{
matrix[j][i] = false;
}
}
}
return matrix[slen][plen];
}
};
其实DP是自然的,但是如果不用一开始的长度判断,依然会超时。
其实长度判断也属于一种剪枝吧,用O(n)的复杂度判断对O(n^2)的DP剪枝,是很大的改善。
由于F[i][j]只和上两个状态有关,所以可使用滚动数组省空间。
网上也有用贪心的,可再研究。
第二刷:
关键在于,只要记录最后一个*就可以了,因为*可以匹配所有,如果*后面的没匹配上,就是*之后的问题,即使退回之前的*,情况只会更差。
class Solution {
public:
bool isMatch(const char *s, const char *p) {
int i = 0;
int j = 0;
int backup_i = -1;
int backup_j = -1;
while (s[i] != ' ') {
if (s[i] == p[j] || p[j] == '?') {
i++; j++;
} else if (p[j] == '*') {
backup_j = j;
backup_i = i;
j++;
} else if (s[i] != p[j]) {
if (backup_j == -1) {
return false;
}
j = backup_j;
i = backup_i + 1;
backup_i = i;
j++;
}
}
if (s[i] == ' ') {// && left all '*' return true;
while (p[j] != ' ') {
if (p[j] != '*') {
return false;
}
j++;
}
return true;
}
return false;
}
};