[leetcode]Wildcard Matching
难题。一开始使用递归+备忘录的方式,我的递归写的太烂,大数据集合超时。要注意的是,一开始没考虑到"", "*"的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 != '\0') 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 == '\0') return true; while(*s != '\0' && !isMatch(s,p)){ ++s; } return *s != '\0'; } else if (*p == '\0' || *s == '\0') 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 != '\0') 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] != '\0') { 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] == '\0') {// && left all '*' return true; while (p[j] != '\0') { if (p[j] != '*') { return false; } j++; } return true; } return false; } };