您的位置:首页 > 其它

Leetcode | Wildcard Matching

2014-05-07 21:34 225 查看
Implement wildcard pattern matching with support for '?' and '*'.

'?' Matches any single character.
'*' Matches any sequence of characters (including the empty sequence).

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", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "c*a*b") → false

递归的话会超时。

Method I

然后想用二维的DP。

借用了discussion上面的图。m[i+1][j+1]就是p[0...i]与s[0...j]的匹配结果。

1. 如果p[i] = '?' 或者 p[i] = s[j] (暗含p[i] != '*'的意思),那么m[i+1][j+1]就是等于它的对角线左上方的位置, m[i][j]。也就是都移动了一个字符。

2. 如果p[i] = '*'。从图里面就可以看出来,从(3)走到(4),其实p[0...3]="a?c*"应该是能够匹配到s[0...j],2<=j<6。也就是说,m[4][3...6]=true;

就是从上一行找到第一个为true的位置,那么从当前行中从该位置到最后都应该设为true。



bool isMatch(const char *s, const char *p) {
if (p == '\0') return (s == '\0');
int n1 = strlen(s);
int n2 = strlen(p);

vector<vector<bool> > m(n2 + 1, vector<bool>(n1 + 1, false));
m[0][0] = true;

for (int i = 0; i < n2; ++i) {
if (p[i] == '*') {
int j = 0;
for (j = 0; j <= n1; ++j) {
if (m[i][j]) break;
}
if (j > n1) return false;
for (int k = j; k <= n1; ++k) {
m[i + 1][k] = true;
}
} else {
for (int j = 0; j < n1; ++j) {
if (p[i] == '?' || s[j] == p[i]) {
m[i + 1][j + 1] = m[i][j];
}
}
}
}

return m[n2][n1];
}


二维DP会报MLE,但是阿牧遥 用了一个dp[500][500]倒是可以通过。。。

优化

因为其实每次都是更新一行,可以将两维dp改成一维的dp。将上面的代码对应转换就行,不过要注意的是,不满足条件更新值的,需要设成默认值false;

class Solution {
public:
bool isMatch(const char *s, const char *p) {
if (p == '\0') return (s == '\0');
int n1 = strlen(s);
int n2 = strlen(p);
int chars = 0;
for(int i=0; p[i] != '\0'; ++i) if(p[i] != '*' && n1<++chars) return false;

vector<bool> m(n1 + 1, false);
m[0] = true;

for (int i = 0; i < n2; ++i) {
if (p[i] == '*') {
int j = 0;
for (j = 0; j <= n1; ++j) {
if (m[j]) break;
}
for (; j <= n1; ++j) {
m[j] = true;
}
} else {
for (int j = n1 - 1; j >= 0; --j) {
if (p[i] == '?' || s[j] == p[i]) {
m[j + 1] = m[j];
} else {
m[j + 1] = 0;
}
}
m[0] = false;
}
}

return m[n1];
}
};


注意第8行,如果没有这一句就会TLE。主要是计算p里面的非*的字符,如果这个数目都比s的长度大,那么肯定匹配不完。直接返回true。这是为了过一个大数据。

Method II

遇到*的时候是可能需要回溯的。但是回溯是不是一定要回到回溯前的位置呢。比如s="aacb",p="a*a*b"。结果应该是为true的。

第一次遇到p[1]='*',从s[1]开始匹配,s[1]=p[2], 第二次遇到p[3] ='*',s[2]!=p[4],此路不通,要回溯,是不是要回溯到p[2]和s[2]呢,其实不用。

因为我们最后面撞见了p[3]='*',证明s[0...1]和p[0...2]是匹配的,那么直接从p[4]和s[3]回溯就可以了。p[4]=s[3],所以返回true。

因为遇到*都是先将*从区配空串开始的,所以当*把s都一直匹配到末尾有三种情况:

1. p后面还有,但是都是*,还是返回true;

2. p后面还有,但不全是*,返回false;

3. p后面没有,匹配刚刚好。

lass Solution {
public:
bool isMatch(const char *s, const char *p) {
if (p == '\0') return (s == '\0');

const char *backP = NULL, *backS = NULL;

while (*s) {
if (*p == '*') {
while (*p != '\0' && *p == '*') p++;
if (*p == NULL) return true;
backS = s;
backP = p;
} else if (*s == *p || *p == '?') {
s++; p++;
} else if (backP) {
s = ++backS;
p = backP;
} else {
return false;
}
}

while (*p) {
if (*p != '*') return false;
p++;
}

return true;
}
};


这里第10行,是压缩了p连续*的情况。多个*相连其实相同于一个*。backP指向的*的下一个字符。backS就是从S当前的位置开始。

third time.

class Solution {
public:
bool isMatch(const char *s, const char *p) {
if (p == NULL || *p == '\0') return (s == NULL || *s == '\0');
const char* lastS = NULL, *lastP = NULL;

while (*s != '\0') {
if (*p != '\0' && (*s == *p || *p == '?')) {
s++, p++;
} else if (*p == '*') {
for (; *p != '\0' && *p == '*'; p++);
lastP = p;
lastS = s;
} else if (lastP != NULL) {
p = lastP;
s = ++lastS;
} else {
break;
}
}

for (; *p != '\0' && *p == '*'; p++);
return *p == '\0' && *s == '\0';
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: