[leetcode] Wildcard Matching 萬用字元匹配
阿新 • • 發佈:2019-02-07
也是《劍指offer》中的題目
問題描述:
判斷兩個可能包含萬用字元“?”和“*”的字串是否匹配。匹配規則如下:
'?' 可以匹配任何單個字元。 '*' 可以匹配任意字串(包括空字串)。 兩個串完全匹配才算匹配成功。
介面如下:其中s為待匹配串(不含'?'和'*'),p為模式串(含'?'和'*')
bool isMatch(const char *s, const char *p)
一些例子:
isMatch("aa", "*") → true isMatch("aa", "a*") → true isMatch("ab", "?*") → true isMatch("aab", "c*a*b") → false
可以發現dp[i][j]為真有三種情況:(1) dp[i-1][j-1]為真,且s[i] 與 s[j]匹配;(2) dp[i-1][j]為真,且p[j]為'*',此時用'*'匹配多個字元;(3) dp[i][j-1]為真,且p[j]為'*',此時'*'匹配空字元。 故有:dp[i][j] = ((dp[i-1][j-1] && equal(s+i,p+j)) || (dp[i-1][j] && (*(p+j)=='*')) || (dp[i][j-1] && (*(p+j)=='*'))); 此處為了方便,我將dp[0][0~n]與dp[0~m][0]初始化為空字串與p的匹配情況和s與空字串的匹配情況,具體程式碼如下:
class Solution { public: /** * @param s: A string * @param p: A string includes "?" and "*" * @return: A boolean */ bool equal(const char *s, const char *p){ if(*s == *p) return true; if(*p == '?' || *p == '*') return true; return false; } bool isMatch(const char *s, const char *p) { // write your code here if(s == NULL && p == NULL) return true; int m = strlen(s); int n = strlen(p); if(m == 0 && n == 0) return true; vector<bool> tmp(n+1,false); vector<vector<bool> > dp(m+1,tmp); dp[0][0] = true; for(int i=0; i<n; ++i){ dp[0][i+1] = (dp[0][i] && (*(p+i) == '*')); } for(int i=0; i<m; ++i){ for(int j=0; j<n; ++j){ dp[i+1][j+1] = ((dp[i][j] && equal(s+i,p+j)) || (dp[i][j+1] && (*(p+j)=='*')) || (dp[i+1][j] && (*(p+j)=='*'))); //printf("dp[%d][%d]=%d ",i+1,j+1,dp[i+1][j+1]?1:0); } //printf("\n"); } return dp[m][n]; } };
(三)後來看了discuss才發現還可以使用貪心演算法,不僅時間上有優化,還將空間壓縮至O(1)。 主要思想是:從兩個字串的起始位置開始匹配,遇到萬用字元 '*' 時,優先考慮讓其匹配空字元,但記錄該 ’*‘ 的位置,以及對應的s指標的位置,然後繼續往後匹配;直到無法繼續,則返回上一個 ’*‘ 的位置,考慮讓其匹配1個字元,然後繼續往後匹配;直到無法匹配,則重新返回上一個 ’*‘ 的位置,考慮讓其匹配2個字元。。。。。。直到s的指標到達終點。此時,若當前p指標後面均為’*‘,則返回true,否則返回false。 程式碼如下:
class Solution { public: /** * @param s: A string * @param p: A string includes "?" and "*" * @return: A boolean */ // greedy method bool isMatch(const char *s, const char *p) { // write your code here if(s == NULL && p == NULL) return true; int m = strlen(s); int n = strlen(p); if(m == 0 && n == 0) return true; int si = 0, pi = 0; int xidx = -1, mtch = -1; while(si < m){ if(pi < n && (*(p+pi)=='*')){ xidx = pi++; mtch = si; // si對應xidx的位置 }else if(pi < n && (*(s+si) == *(p+pi) || *(p+pi) == '?')){ ++ si; ++ pi; }else if(xidx > -1){ // 上一個 '*' 的位置 pi = xidx + 1; si = ++ mtch; }else{ return false; } } while(pi < n && (*(p+pi) == '*')) ++ pi; return (pi == n); } };