您的位置:首页 > 其它

Wildcard Matching[leetcode]直接匹配和DP

2014-09-15 16:20 344 查看
这题类似
Regular Expression Matching,但是数据比较强。

首先介绍DP的解法,回忆Regular Expression Matching,我们也用dp(i,j)表示s[0...i-1]和p[0...j-1]是否匹配

基本情况相似,但是更简单:

1. dp(0,0) = true

2. dp(0,j) = dp(0,j-1) && p[j-1] == '*‘

3. dp(i,0) = false

一般情况dp(i,j):

1. dp(i-1,j-1) && (s[i-1] == p[j-1] || p[j-1] == '?')

2. dp(i, j-1) && p[j-1] == '*'

3. dp(i-1, j) && p[j-1] == '*'

由于第i行只用到了第i-1行的数据,所以可以将空间上压缩至一维。我们用一个bool curRow表示当前访问的行号,!curRow表示i-1行

第一个版本

bool isMatch(const char *s, const char *p) {
int m = strlen(s);
int n = strlen(p);
bool curR = 0;
vector<vector<bool>> dp(2);
dp[curR] = (vector<bool>(n+1));
dp[!curR] = (vector<bool>(n+1));
dp[0][0] = true;
for (int i = 1; i <= n; i++)
dp[0][i] = (p[i-1] == '*' && dp[0][i-1]);

const char* tmp = p;
int cnt = 0;
while (*tmp != '\0') if (*(tmp++) != '*') cnt++;
if (cnt > m) return false;

for (int i = 1; i <= m; i++)
{
curR = !curR;
dp[curR][0]=false;
for (int j = 1; j <= n; j++)
{
dp[curR][j] = (dp[!curR][j-1] && (s[i-1] == p[j-1] || p[j-1] == '?') ||
dp[curR][j-1] && p[j-1] == '*' ||
dp[!curR][j] && p[j-1] == '*');
}
}
return dp[curR]
;
}


注意这里有一个剪枝,if (cnt > m) return false,用于迅速将不符合的情况排除。如果没有这一段的话会TLE。

第二个版本

bool isMatch(const char *s, const char *p) {
int m = strlen(s);
int n = strlen(p);
if (!quickElimination(s,p)) return false;
bool curR = 0;
vector<vector<bool>> dp(2);
dp[curR] = (vector<bool>(n+1));
dp[!curR] = (vector<bool>(n+1));
dp[0][0] = true;
for (int i = 1; i <= n; i++)
dp[0][i] = (p[i-1] == '*' && dp[0][i-1]);

for (int i = 1; i <= m; i++)
{
curR = !curR;
dp[curR][0]=false;
for (int j = 1; j <= n; j++)
{
if (p[j-1] == '*')
dp[curR][j] = dp[curR][j-1] || dp[!curR][j];
else if (s[i-1] == p[j-1] || p[j-1] == '?')
dp[curR][j] = dp[!curR][j-1];
else
dp[curR][j] = false;
}
}
return dp[curR]
;
}

bool quickElimination(const char *s, const char *p)
{
int cnt = 0;
while (*p != '\0') if (*(p++) != '*') cnt++;
if (cnt > strlen(s)) return false;
return true;
}
主要的不同是在计算dp(i,j)时用if-else取代一系列的逻辑运算符,速度上提高30%左右。因为第一个版本在计算时,某个语句失败后会计算以||相连的下一个条件,造成更多运算。

复杂度为O(slen*plen)

直接匹配的方法

http://yucoding.blogspot.com/2013/02/leetcode-question-123-wildcard-matching.html上有个非常简单优美的方法,比我第一次几十行的代码优秀太多。
    bool isMatch(const char *s, const char *p) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
         
        const char* star=NULL;
        const char* ss=s;
        while (*s){
            if ((*p=='?')||(*p==*s)){s++;p++;continue;}
            if (*p=='*'){star=p++; ss=s;continue;}
            if (star){ p = star+1; s=++ss;continue;}
            return false;
        }
        while (*p=='*'){p++;}
        return !*p;
    }
主要的思想是:

1.*s==*p or *p == ? :匹配,s++ p++。

2. p=='*':也是一个匹配,但可能有0个或者多个字符用于匹配,所以将'*'所在位置保存(star),并且将s的位置也保存下来(ss)。

3. 如果不匹配,查看之前是否有'*'。没有则返回false,有则对ss的下一个位置和start之后的重新匹配。

暴力法,复杂度是O(slen*slen)。

由于例子中plen和slen相差不大,而且暴力法在搜索途中就能发现不匹配的字符串,终止搜索;而DP需要在遍历完所有子问题后才能给出解,所以第二种方法快得多。

OJ上,暴力法耗时116ms,DP耗时720ms
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: