44. Wildcard Matching (String; DP, Back-Track)
Implement wildcard pattern matching with support for '?'
and '*'
.
'?' Matches any single character.
'*' Matches any sequence of characters (including the empty sequence).不同于正则表达式中的*
*正则表达式的定义:
- '.' Matches any single character.
- '*' Matches zero or more of the preceding element.
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
思路I:当遇到*,有把*跳过,和继续保留*两种option=>带回溯的递归。其实也可称之为贪心法,贪心法则是每次都使*匹配尽可能少的字符。
class Solution { public: bool isMatch(string s, string p) { return backTracking(s,p,0,0); } bool backTracking(string s, string p, int sp, int pp){ //end condition if(sp==s.length()){ while(pp<p.length() &&p[pp]=='*' ){ pp++; } if(pp == p.length()) return true; else return false; } if(pp==p.length()) return false; if(p[pp]=='*'){ while(pp+1<p.length() && p[pp+1]=='*') pp++; //ignore the stars directly behind star if(backTracking(s,p,sp,pp+1)) return true; //* not repeats return backTracking(s,p,sp+1,pp); //* repeats } else if(s[sp]==p[pp] || p[pp]=='?') return backTracking(s,p,sp+1,pp+1); else return false; } };
时间复杂度:二叉recursion的高度是2n 所以O(2n)
Result: Time Limit Exceeded
思路II:依然是带回溯的递归,只是记录下*号位置,和匹配的字符数,那么等到某次*不匹配时可直接回到该位置。
class Solution { public: bool isMatch(string s, string p) { star = false; return recursiveCheck(s,p,0,0); } bool recursiveCheck(const string &s, const string &p, int sIndex, int pIndex){ if(sIndex >= s.length()){ while(p[pIndex] == '*' && pIndex < p.length()) pIndex++; //s has went to end, check if the rest of p are all * return (pIndex==p.length()); } if(pIndex >= p.length()){ return checkStar(s,p); } switch(p[pIndex]) //p: pattern,在p中才可能出现?, * { case '?': return recursiveCheck(s, p, sIndex+1, pIndex+1); break; case '*': //如果当前为*, 那么可认为之前的字符都匹配上了,并且将p移动到 * 结束后的第一个字符 star = true; //p 每次指向的位置,要么是最开始,要么是 * 结束的第一个位置 starIndex = pIndex; matchedIndex = sIndex-1; while(p[pIndex] == '*'&& pIndex < p.length()){pIndex++;} //忽略紧接在 *后面的* if(pIndex==p.length()) return true;//最后一位是* return recursiveCheck(s,p,sIndex,pIndex); //*匹配0个字符 break; default: if(s[sIndex] != p[pIndex]) return checkStar(s, p); else return recursiveCheck(s, p, sIndex+1, pIndex+1); break; } } bool checkStar(const string &s, const string &p){ if(!star) return false; else { int pIndex = starIndex+1; int sIndex = ++matchedIndex; //回溯,*d多匹配一个字符 return recursiveCheck(s, p, sIndex, pIndex); } } private: int starIndex; int matchedIndex; bool star; };
Result: Approved.
思路III:使用dp。dp[i][j]表示从字符串到i位置,模式串到j位置是否匹配。
class Solution { public: bool isMatch(string s, string p) { int sLen = s.length(); int pLen = p.length(); if(sLen == 0){ int pp = 0; while(pp<p.length() &&p[pp]=='*' ){ pp++; } if(pp == p.length()) return true; else return false; } if(pLen == 0) return false; int len = 0; for(int i = 0;i < pLen;i++) if(p[i] != '*') len++; if(len > sLen) return false; bool dp[sLen][pLen]; int i = 0, j = 0; for(;i<sLen;i++){ for(;j<pLen;j++){ dp[i][j]=false; } } if(p[0]=='*'){ //c;*?* for(i = 0;i < sLen; i++ ){ dp[i][0] = true; } } //first line can appear one letter which is not star if (p[0]=='?' || s[0] == p[0]){ //first not-star-letter appears dp[0][0] = true; for(j = 1;(j < pLen && p[j]=='*'); j++ ){ dp[0][j]=true; } } else if(p[0]=='*'){ for(j = 1;(j < pLen && p[j-1]=='*'); j++ ){ if(p[j]=='?' || s[0] == p[j]){ //first not-star-letter appears dp[0][j]=true; j++; for(;j<pLen && p[j]=='*'; j++){ //after first not star, there should be all star dp[0][j]=true; } break; } else if(p[j]=='*'){ dp[0][j]=true; } } } for(i = 1; i < sLen; i++){ for(j = 1; j < pLen; j++){ if(p[j]=='*'){ dp[i][j] = dp[i-1][j] //* repeat 1 time || dp[i][j-1]; //*repeat 0 times } else if(s[i]==p[j] || p[j]=='?'){ dp[i][j] = dp[i-1][j-1]; } } } return dp[sLen-1][pLen-1]; } };
时间复杂度:O(n2)
思路IV: 思路III的初始状态求法太复杂=>Solution:定义一个fake head。dp[0][0]表示两个空字符串的匹配情况,dp[0][0]=true.
class Solution { public: bool isMatch(string s, string p) { int sLen = s.length(); int pLen = p.length(); if(sLen == 0){ int pp = 0; while(pp<p.length() &&p[pp]=='*' ){ pp++; } if(pp == p.length()) return true; else return false; } if(pLen == 0) return false; vector<vector<bool>> dp(sLen+1, vector<bool>(pLen+1,0)); //initial states int i = 0, j = 0; dp[0][0]=true; for(j = 1;(j <= pLen && p[j-1]=='*'); j++ ){ dp[0][j]=true; } //state transfer for(i = 1; i <= sLen; i++){ for(j = 1; j <= pLen; j++){ if(p[j-1]=='*'){ dp[i][j] = dp[i-1][j] //* repeat 1 time || dp[i][j-1]; //*repeat 0 times } else if(s[i-1]==p[j-1] || p[j-1]=='?'){ dp[i][j] = dp[i-1][j-1]; } } } return dp[sLen][pLen]; } };
思路V:节约空间,状态之和i-1有关,所以只要记录上一行状态就可以。可以用一维数组。
class Solution { public: bool isMatch(string s, string p) { int sLen = s.length(); int pLen = p.length(); if(sLen == 0){ int pp = 0; while(pp<p.length() &&p[pp]=='*' ){ pp++; } if(pp == p.length()) return true; else return false; } if(pLen == 0) return false; vector<bool> lastDP(pLen+1, 0); vector<bool> currentDP(pLen+1, 0); vector<bool> tmp; //initial states int i = 0, j = 0; lastDP[0]=true; for(j = 1;(j <= pLen && p[j-1]=='*'); j++ ){ lastDP[j]=true; } //state transfer for(i = 1; i <= sLen; i++){ currentDP[0]=false; for(j = 1; j <= pLen; j++){ if(p[j-1]=='*'){ currentDP[j] = lastDP[j] //* repeat 1 time || currentDP[j-1]; //*repeat 0 times } else if(s[i-1]==p[j-1] || p[j-1]=='?'){ currentDP[j] = lastDP[j-1]; } else{ currentDP[j] = false; } } tmp = currentDP; currentDP = lastDP; lastDP = tmp; } return lastDP[pLen]; } };