题目:
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
代码:
class Solution {
public:
bool isMatch(string s, string p) {
const size_t len_s = s.length();
const size_t len_p = p.length();
if ( len_s>= ) return false; // escape the last extreme large test case
bool dp[len_s+][len_p+];
// dp initialization
dp[][] = true;
for ( size_t i = ; i <=len_s; ++i ) dp[i][]=false;
for ( size_t i = ; i <=len_p; ++i ) dp[][i] = p[i-]=='*' && dp[][i-];
// dp process
for ( size_t i = ; i <=len_s; ++i ){
for ( size_t j = ; j <=len_p; ++j ){
if ( p[j-]!='*'){
dp[i][j] = dp[i-][j-] && ( p[j-]==s[i-] || p[j-]=='?' );
}
else{
dp[i][j] = dp[i-][j] || dp[i-][j-] || dp[i][j-];
}
}
}
return dp[len_s][len_p];
}
};
tips:
采用动态规划的思路,模仿Regular Expression Matching这道题的思路。
判断p的下一个元素是不是‘*’,分两种情况讨论。
这里需要注意的是如果p的下一个元素是‘*’,则需要讨论从不同之前状态转移到dp[i][j]的几种case。这道题由于放宽了对*的限制,所以判断的情况比较直观了:dp[i-1][j] dp[i-1][j-1] dp[i][j-1]只要有一个成立dp[i][j]就为真了。
但是第1801个test case是超大集合,s超过30000个字符,所以直接跳过了。
这道题其实可以尝试一下Greedy算法,兴许就不用作弊,而且空间复杂度降低了。
==============================================
无力再去想Greedy算法了,直接参考一个 extremely elegant的c++ code
这是原创blog:http://yucoding.blogspot.sg/2013/02/leetcode-question-123-wildcard-matching.html
上面原创的blog在leetcode的discuss上被解读了一遍:https://leetcode.com/discuss/10133/linear-runtime-and-constant-space-solution
参照上面两个blog,按照leetcode上新版的接口,写的代码如下:
class Solution {
public:
bool isMatch(string s, string p) {
const size_t len_s = s.length();
const size_t len_p = p.length();
size_t index_s = ;
size_t index_p = ;
size_t index_star = INT_MIN;
size_t index_match_by_star = ;
while ( index_s < len_s )
{
if ( (index_p<len_p && p[index_p]=='?') || p[index_p]==s[index_s] )
{
++index_s;
++index_p;
continue;
}
if ( index_p<len_p && p[index_p]=='*' )
{
index_star = index_p;
++index_p;
index_match_by_star = index_s;
continue;
}
if ( index_star!=INT_MIN )
{
index_p = index_star + ;
index_s = index_match_by_star + ;
++index_match_by_star;
continue;
}
return false;
}
while ( index_p<len_p && p[index_p]=='*' ) ++index_p;
return index_p==len_p;
}
};
思路和代码逻辑完全按照blog里面大神的逻辑,大神的思路巧妙,代码consice,只能膜拜并模仿了。
这里面要有一个地方注意一下:由于接口参数由char *改为了string,因此最后一个'\0'就没有了。
所以,不能直接使用p[index_p],每次使用p[index_p]之前,需要判断index_p<len_p这个条件,这样就可以不用跳过最后一组数据并且AC了。
正则匹配这个告一段落,个人感觉还是dp的思路通用性好一些:如果能列出来状态转移的表达式,就可以不断尝试转移的case,最终使得代码AC。
=============================================================
第二次过这道题,直接照着之前的dp代码弄的。思路就记住就好了。
class Solution {
public:
bool isMatch(string s, string p) {
if ( s.size()>= ) return false;
bool dp[s.size()+][p.size()+];
std::fill_n(&dp[][], (s.size()+)*(p.size()+), false);
dp[][] = true;
for ( int j=; j<=p.size(); ++j )
{
dp[][j] = dp[][j-] && p[j-]=='*';
}
for ( int i=; i<=s.size(); ++i )
{
for ( int j=; j<=p.size(); ++j )
{
if (p[j-]!='*')
{
dp[i][j] = dp[i-][j-] && ( s[i-]==p[j-] || p[j-]=='?' );
}
else
{
dp[i][j] = dp[i-][j-] || dp[i-][j] || dp[i][j-];
}
}
}
return dp[s.size()][p.size()];
}
};