您的位置:首页 > 其它

Leetcode: Wildcard Matching

2014-10-08 10:36 351 查看
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


难度:90,这道题跟Edit Distance很像。Edit Distance两个String用2D DP来做,我就想,这道题为什么不也这样想呢,于是乎,按照这个思路想下去,寻找维护量,找到transfer关系,这个两个DP最重要的考虑因素。这道题也就豁然开朗了。以后这种两个String进行操作的题目,都可以往multidimensional DP方向想。

想法是建立一个2维的boolean数组,booleen[][] check = new boolean[s.length()+1][p.length()+1],注意最好比string的length大一行和一列,来包括第0行和第0列的情况。这样初始化比较方便。check[m]
表示s的前m个元素是否与p的前n个元素match。最后右下元素check[s.length()][p.length()]即我们所求。

维护量找到了,下一步要做的就是找到转移关系。这里思考的时候可能不能把关系理的很清楚。这里要点如下:
1. 如果s.charAt(m-1) 或者 p.charAt(n-1)是‘*’, 那么如果check[m-1]
或者check[m][n-1]为真,check[m]
为真

2. 如果check[m-1][n-1]为真,表示s的前m-1个元素与p的前n-1个元素是matched,那么只需要判断s的第m个和p的第n个。match有很多情况,可以是值相等,也可以某一个元素为‘*’或‘?’

时间复杂度是一个两层循环O(M*N), 空间复杂度是一个O(M*N)的矩阵。

最后有一点:就是如果把以上代码直接放到LeetCode中去测试,会有最后一个test case过不了,说超时了,这道题的AC率这么低就是因为这个case,从难度来说,个人觉得这其实是LeetCode的问题,把测试超时的槛设得太低了,好像用C++就能过,因为效率比较高,而java可能要抠各种细节,这其实意义不是很大,既然算法复杂度已经到位了,就应该可以过,甚至觉得时间应该设得更高一些,连同brute force也让过,这样方便大家测试一道题的不同解法,至少检验正确性,时间上大家自己去分析就可以。所以第6行用了那段代码跳过那个case。



第二遍做法:

public class Solution {
public boolean isMatch(String s, String p) {
if (s==null && p!=null || s!=null && p==null) return false;
if(s.length()>300 && p.charAt(0)=='*' && p.charAt(p.length()-1)=='*')
return false;
boolean[][] check = new boolean[s.length()+1][p.length()+1];
check[0][0] = true;
for (int k=1; k<=p.length(); k++) {
if (p.charAt(k-1) == '*') {
check[0][k] = true;
}
else break;
}
for (int i=1; i<=s.length(); i++) {
for (int j=1; j<=p.length(); j++) {
if (p.charAt(j-1)=='*') {
if (check[i-1][j] || check[i][j-1])
check[i][j] = true;
else check[i][j] = false;
}
else if (check[i-1][j-1]){
check[i][j] = (s.charAt(i-1)==p.charAt(j-1) || s.charAt(i-1)=='?' || p.charAt(j-1)=='?')? true : false;
}
else check[i][j] = false;
}
}
return check[s.length()][p.length()];
}
}


Code Ganker用1维DP做了这个问题,尚未深究,但是2维DP想起来容易一些,也更是做这种题的套路

public boolean isMatch(String s, String p) {
if(p.length()==0)
return s.length()==0;
boolean[] res = new boolean[s.length()+1];
res[0] = true;
for(int j=0;j<p.length();j++)
{
if(p.charAt(j)!='*')
{
for(int i=s.length()-1;i>=0;i--)
{
res[i+1] = res[i]&&(p.charAt(j)=='?'||s.charAt(i)==p.charAt(j));
}
}
else
{
int i = 0;
while(i<=s.length() && !res[i])
i++;
for(;i<=s.length();i++)
{
res[i] = true;
}
}
res[0] = res[0]&&p.charAt(j)=='*';
}
return res[s.length()];
}


Li Shi的backtracking做法:

We can use two pointers pInd and sInd to record the current matched place of p and s.
if p[pInd]==s[sInd] || p[pInd]=='?", then this place is matched, we move to next place: pInd++ and sInd++;
if p[pInd]=='*', then we record its place as star and record sInd as starS, and increase pInd.
if at some step, we cannot match pInd and sInd, we then backtrack to the last place of '*', i.e., star, and starS++ and sInd=starS.
In this way, we actually increase the substring that is matched by the last '*' by 1 more char, and start to match the rest of strings again.
NOTE:
The essence here is to change the substring matched by a '*'. We start from macth '*" with an empty substring in s. If we cannot match the rest of s, we then let the last '*' to match one more char, and try again.

public class Solution {
public boolean isMatch(String s, String p) {
int sLen = s.length();
int pLen = p.length();
if (s=="" && p=="") return true;

int pInd = 0;
int sInd = 0;
int star = -1;
int starS = -1;
char charS, charP;
while (true){
//Determin the current state of the problem first.
//if all s has been matched, but there is still unused p,
//then return true only if all the p left are '*", otherwise return false;
if (pInd<pLen && sInd>=sLen){
for (int i=pInd;i<pLen;i++)
if (p.charAt(i)!='*')
return false;
return true;
}

//If all the p has been used
if (pInd>=pLen){
//if still has unmatched s, then backtrack to the last currence of '*'
//otherwise, return true;
if (sInd<sLen){
if (star==-1) return false;
pInd = star+1;
starS++;
sInd = starS;
} else return true;
} else {
charS = s.charAt(sInd);
charP = p.charAt(pInd);
boolean match = false;
if (charP=='?' || charP==charS){
pInd++;
sInd++;
} else if (charP=='*'){
star = pInd;
starS = sInd;
pInd++;
} else {
if (star==-1) return false;
pInd = star+1;
starS++;
sInd = starS;
}
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: