Related: LeetCode 10 - Regular Expression Matching
Wildcard Matching, Leetcode 解题笔记 | Simple & Stupid
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).
https://www.cnblogs.com/grandyang/p/4401196.html
X. Two pointers
http://happycoding2010.blogspot.com/2015/09/leetcode-43-wildcard-matching.html
https://yujia.io/blog/2015/11/15/LeetCode-44-Wildcard-Matching/
http://www.programcreek.com/2014/06/leetcode-wildcard-matching-java/
https://leetcode.com/discuss/10133/linear-runtime-and-constant-space-solution
if (*p=='*'){star=p++; ss=s;continue;} if (star){ p = star+1; s=++ss;continue;} //pointer s will go back , so not O(n).
X. DP backward
https://blog.csdn.net/aximi/article/details/83898167
https://leetcode.com/problems/wildcard-matching/discuss/17812/My-java-DP-solution-using-2D-table
X. O(n) space
https://leetcode.com/problems/wildcard-matching/discuss/17871/O(n)-space-DP-using-two-1-d-array-to-rotate.
然后用一维滚动数组改写,发现会Time Limit Exceeded
http://happycoding2010.blogspot.com/2015/09/leetcode-43-wildcard-matching.html
http://likesky3.iteye.com/blog/2217581
https://discuss.leetcode.com/topic/10794/my-java-dp-solution
http://blog.csdn.net/linhuanmars/article/details/21198049
http://shmilyaw-hotmail-com.iteye.com/blog/2154716
在不考虑"*"的情况下,我们的方法大致结构如下:
对于字符串的模糊匹配问题看起来很简单,实际上牵涉到非常复杂的判断条件。
结合不同的场景,最关键的就是要找到它们的递归关系,
然后进行动态规划方法的优化。
http://zjalgorithm.blogspot.com/2014/11/leetcode-java-wildcard-matching.html
Recursive Version:
http://vialgorithms.blogspot.com/2013/11/wildcard-matching.html
http://www.cnblogs.com/yuzhangcmu/p/4116153.html
动态规划:输入两个字符串s,p(p包含通配符,用p去匹配s),用flag[i][j]表示字符串p从0到i的的子字符串能否匹配s从0到j的子字符串,我们可以得到如下递推公式:
如果p.charAt(i)==s.charAt(j)或则p.charAt(i)=='?',相当于将最后一个字符匹配掉,所以
flag[i][j]=flag[i-1][j-1];
如果p.charAt(i)=='*','*'可以选择匹配0个字符,此时flag[i][j]=flag[i-1][j];可以选择匹配1个字符,此时flag[i][j]=flag[i-1][j-1];……所以,
flag[i][j]=flag[i-1][j]||flag[i-1][j-1]||……||flag[i-1][0]。
但是上面的公式可以化简,当p.charAt(i)=='*'时,有
flag[i][j-1]=flag[i-1][j-1]||flag[i-1][j-2]||……||flag[i-1][0]
所以 flag[i][j]==flag[i-1][j]||flag[i][j-1],所以综合递推公式如下:
空间复杂度?
由上面的公式可以看出,flag[i][j]只与第i行和第i-1行相关,所以只需要开辟两个一维数组即可。空间复杂度是O(n),时间复杂度是O(mn)。
http://codeganker.blogspot.com/2014/03/wildcard-matching-leetcode.html
DP: SPace:O(n)(1)p[j]不是'*'。情况比较简单,只要判断如果当前s的i和p的j上的字符一样(如果有p在j上的字符是'?',也是相同),并且res[i]==true,则更新res[i+1]为true,否则res[i+1]=false;
(2)p[j]是'*'。因为'*'可以匹配任意字符串,所以在前面的res[i]只要有true,那么剩下的 res[i+1], res[i+2],...,res[s.length()]就都是true了。
算法的时间复杂度因为是两层循环,所以是O(m*n), 而空间复杂度只用一个一维数组,所以是O(n)
X. DFS
http://www.voidcn.com/article/p-ehnxhspt-gt.html
Wildcard Matching, Leetcode 解题笔记 | Simple & Stupid
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
https://www.cnblogs.com/grandyang/p/4401196.html
这道题也能用动态规划Dynamic Programming来解,写法跟之前那道题 Regular Expression Matching 很像,但是还是不一样。外卡匹配和正则匹配最大的区别就是在星号的使用规则上,对于正则匹配来说,星号不能单独存在,前面必须要有一个字符,而星号存在的意义就是表明前面这个字符的个数可以是任意个,包括0个,那么就是说即使前面这个字符并没有在s中出现过也无所谓,只要后面的能匹配上就可以了。而外卡匹配就不是这样的,外卡匹配中的星号跟前面的字符没有半毛钱关系,如果前面的字符没有匹配上,那么直接返回false了,根本不用管星号。而星号存在的作用是可以表示任意的字符串,当然只是当匹配字符串缺少一些字符的时候起作用,当匹配字符串p包含目标字符串s中没有的字符时,将无法成功匹配。
对于这种玩字符串的题目,动态规划Dynamic Programming是一大神器,因为字符串跟其子串之间的关系十分密切,正好适合DP这种靠推导状态转移方程的特性。那么先来定义dp数组吧,我们使用一个二维dp数组,其中 dp[i][j] 表示 s中前i个字符组成的子串和p中前j个字符组成的子串是否能匹配。大小初始化为 (m+1) x (n+1),加1的原因是要包含dp[0][0] 的情况,因为若s和p都为空的话,也应该返回true,所以也要初始化 dp[0][0] 为true。还需要提前处理的一种情况是,当s为空,p为连续的星号时的情况。由于星号是可以代表空串的,所以只要s为空,那么连续的星号的位置都应该为true,所以我们现将连续星号的位置都赋为true。然后就是推导一般的状态转移方程了,如何更新 dp[i][j],首先处理比较tricky的情况,若p中第j个字符是星号,由于星号可以匹配空串,所以如果p中的前j-1个字符跟s中前i个字符匹配成功了(dp[i][j-1] 为true)的话,那么 dp[i][j] 也能为true。或者若 p中的前j个字符跟s中的前i-1个字符匹配成功了(dp[i-1][j] 为true)的话,那么 dp[i][j] 也能为true(因为星号可以匹配任意字符串,再多加一个任意字符也没问题)。若p中的第j个字符不是星号,对于一般情况,我们假设已经知道了s中前i-1个字符和p中前j-1个字符的匹配情况(即 dp[i-1][j-1] ),那么现在只需要匹配s中的第i个字符跟p中的第j个字符,若二者相等(s[i-1] == p[j-1]),或者p中的第j个字符是问号(p[j-1] == '?'),再与上 dp[i-1][j-1] 的值,就可以更新 dp[i][j] 了
X.X. Two pointers
http://happycoding2010.blogspot.com/2015/09/leetcode-43-wildcard-matching.html
https://yujia.io/blog/2015/11/15/LeetCode-44-Wildcard-Matching/
In this specific problem, the DP algorithm doesn’t use the fact that if we meet another
'*'
, then the last '*'
we meet can be ignore. I use a Two-Pointer algorithm to get a linear time algorithm.
Set
i
, j
as the index for s
and p
. They goes from and respectively. Let lastp
be the index before which we met '*'
. And lasts
be the index of the s
which we try to match with p[lastp]
. At first we initialize lastp
and lasts
to be . Also we set i=0
and j=0
.
As long as
i < n
, we run the loop. There are three possible cases:s[i] == p[j]
orp[j] == '?'
. Then we match these two character and addi
andj
by 1.p[j] == '*'
. Then setlastp = j+1
andlasts = i
. (Because we can match'*'
with empty)lastp != -1
. If we are in this case, then it means we need to rematch the previous'*'
. Setlasts+=1
andj=lastp
.
When the loop terminate, we need to check the rest element in
p[j:end]
. If all elements in p[j:end]
is '*'
, then it should be true. Otherwise, it cannot match. Why? Because in this algorithm, once we met a '*'
in p
, we ignore the previous '*'
and start using it to match the remaining string. It’s kind of greedy. If there is a non '*'
left in p[j:end]
, then it means we cannot find a way to match p
with s
.
Since in each times of loop, we at least add either
So the overall time complexity will be . It’s much better than the DP algorithm above.
Space complexity is which is the same as the input.
i
or j
by one.So the overall time complexity will be . It’s much better than the DP algorithm above.
Space complexity is which is the same as the input.
bool isMatch(string s, string p) { auto n = s.size(), m = p.size(); int j = 0; for(int i = 0, lastp = -1, lasts = -1; i != n;){ if ((s[i] == p[j]) || (p[j] == '?')){ i++; j++; }else if (p[j] == '*'){ j++; lastp = j; lasts = i; }else if (lastp != -1){ j = lastp; i = ++lasts; }else return false; } for (; (j < m) && (p[j] == '*'); j++); return (j == m); }
http://www.programcreek.com/2014/06/leetcode-wildcard-matching-java/
public boolean isMatch(String s, String p) { int i = 0; int j = 0; int starIndex = -1; int iIndex = -1; while (i < s.length()) { if (j < p.length() && (p.charAt(j) == '?' || p.charAt(j) == s.charAt(i))) { ++i; ++j; } else if (j < p.length() && p.charAt(j) == '*') { starIndex = j; iIndex = i; j++; } else if (starIndex != -1) { j = starIndex + 1; i = iIndex+1; iIndex++; } else { return false; } } while (j < p.length() && p.charAt(j) == '*') { ++j; } return j == p.length(); } |
The basic idea is to have one pointer for the string and one pointer for the pattern. This algorithm iterates at most length(string) + length(pattern) times, for each iteration, at least one pointer advance one step.
For each element in s
If *s==*p or *p == ? which means this is a match, then goes to next element s++ p++.
If p=='*', this is also a match, but one or many chars may be available, so let us save this *'s position and the matched s position.
If not match, then we check if there is a * previously showed up,
if there is no *, return false;
if there is an *, we set current p to the next element of *, and set current s to the next saved s position.
e.g.
abed
?b*d**
a=?, go on, b=b, go on,
e=*, save * position star=3, save s position ss = 3, p++
e!=d, check if there was a *, yes, ss++, s=ss; p=star+1
d=d, go on, meet the end.
check the rest element in p, if all are *, true, else false;
Note that in char array, the last is NOT NULL, to check the end, use "*p" or "*p=='\0'".
boolean comparison(String str, String pattern) {
int s = 0, p = 0, match = 0, starIdx = -1;
while (s < str.length()){
// advancing both pointers
if (p < pattern.length() && (pattern.charAt(p) == '?' || str.charAt(s) == pattern.charAt(p))){
s++;
p++;
}
// * found, only advancing pattern pointer
else if (p < pattern.length() && pattern.charAt(p) == '*'){
starIdx = p;
match = s;
p++;
}
// last pattern pointer was *, advancing string pointer
else if (starIdx != -1){
p = starIdx + 1;
match++;
s = match;
}
//current pattern pointer is not star, last patter pointer was not *
//characters do not match
else return false;
}
//check for remaining characters in pattern
while (p < pattern.length() && pattern.charAt(p) == '*')
p++;
return p == pattern.length();
}If *s==*p or *p == ? which means this is a match, then goes to next element s++ p++.
If p=='*', this is also a match, but one or many chars may be available, so let us save this *'s position and the matched s position.
If not match, then we check if there is a * previously showed up,
if there is no *, return false;
if there is an *, we set current p to the next element of *, and set current s to the next saved s position.
e.g.
abed
?b*d**
a=?, go on, b=b, go on,
e=*, save * position star=3, save s position ss = 3, p++
e!=d, check if there was a *, yes, ss++, s=ss; p=star+1
d=d, go on, meet the end.
check the rest element in p, if all are *, true, else false;
Note that in char array, the last is NOT NULL, to check the end, use "*p" or "*p=='\0'".
if (*p=='*'){star=p++; ss=s;continue;} if (star){ p = star+1; s=++ss;continue;} //pointer s will go back , so not O(n).
For example, s = "aaaaaaaaaaaaaaaab", p = "a*aaaaaaab", it's O(mn)
https://discuss.leetcode.com/topic/3040/linear-runtime-and-constant-space-solution
The basic idea is to have one pointer for the string and one pointer for the pattern. This algorithm iterates at most length(string) + length(pattern) times, for each iteration, at least one pointer advance one step.
boolean comparison(String str, String pattern) {
int s = 0, p = 0, match = 0, starIdx = -1;
while (s < str.length()){
// advancing both pointers
if (p < pattern.length() && (pattern.charAt(p) == '?' || str.charAt(s) == pattern.charAt(p))){
s++;
p++;
}
// * found, only advancing pattern pointer
else if (p < pattern.length() && pattern.charAt(p) == '*'){
starIdx = p;
match = s;
p++;
}
// last pattern pointer was *, advancing string pointer
else if (starIdx != -1){
p = starIdx + 1;
match++;
s = match;
}
//current pattern pointer is not star, last patter pointer was not *
//characters do not match
else return false;
}
//check for remaining characters in pattern
while (p < pattern.length() && pattern.charAt(p) == '*')
p++;
return p == pattern.length();
}
X. DP
public boolean isMatch(String s, String p) {
int count = 0;
for (char c : p.toCharArray()) {
if (c == '*')
count++;
}
if (p.length() - count > s.length())
return false;
boolean[][] dp = new boolean[p.length() + 1][s.length() + 1];
dp[0][0] = true;
for (int j = 1; j <= p.length(); j++) {
char pattern = p.charAt(j - 1);
dp[j][0] = dp[j - 1][0] && pattern == '*';
for (int i = 1; i <= s.length(); i++) {
char letter = s.charAt(i - 1);
if (pattern != '*') {
dp[j][i] = dp[j - 1][i - 1] && (pattern == '?' || pattern == letter);
} else
dp[j][i] = dp[j][i - 1] || dp[j - 1][i];
}
}
return dp[p.length()][s.length()];
}
The key of the problem is to understand the '*', which is able to match ANY sequence of characters. e.g. isMatch(abcd, *) -> true. Note that * does not require the same character in the sequence, as was required by the regular expression matching.
We use DP to solve this problem.
-- Define dp[s.length() + 1][p.length() + 1], where dp[i[j] means the first i characters in string s matches the first characters of string p.
-- Initialization: dp[0][0] = true;
dp[i][0] = false;
dp[0][j] = dp [0][j - 1] IFF p.charAt(j - 1) == '*'
-- Transit function:
-- If p.charAt(j - 1) != '*', then dp[i][j] = dp[i - 1][j - 1] IFF s.charAt(i) == p.charAt(j) || p.charAt(j) == '?'
-- If p.charAt(j - 1) == '*', then
-- dp[i][j] = dp[i - 1][j - 1] || // Match 1 character
= dp[i][j - 1] || // Match 0 character
= dp[i - 1][j] // Match any sequence of characters
-- Return dp[s.length()][p.length()].
We use DP to solve this problem.
-- Define dp[s.length() + 1][p.length() + 1], where dp[i[j] means the first i characters in string s matches the first characters of string p.
-- Initialization: dp[0][0] = true;
dp[i][0] = false;
dp[0][j] = dp [0][j - 1] IFF p.charAt(j - 1) == '*'
-- Transit function:
-- If p.charAt(j - 1) != '*', then dp[i][j] = dp[i - 1][j - 1] IFF s.charAt(i) == p.charAt(j) || p.charAt(j) == '?'
-- If p.charAt(j - 1) == '*', then
-- dp[i][j] = dp[i - 1][j - 1] || // Match 1 character
= dp[i][j - 1] || // Match 0 character
= dp[i - 1][j] // Match any sequence of characters
-- Return dp[s.length()][p.length()].
public
boolean
isMatch(String s, String p) {
if
(p ==
null
|| p.length() ==
0
) {
return
s ==
null
|| s.length() ==
0
;
}
int
rows = s.length();
int
cols = p.length();
boolean
[][] dp =
new
boolean
[rows +
1
][cols +
1
];
dp[
0
][
0
] =
true
;
for
(
int
j =
1
; j <= cols; j++) {
if
(p.charAt(j -
1
) ==
'*'
) {
dp[
0
][j] = dp[
0
][j -
1
];
}
}
for
(
int
i =
1
; i <= rows; i++) {
for
(
int
j =
1
; j <= cols; j++) {
if
(p.charAt(j -
1
) !=
'*'
) {
if
(s.charAt(i -
1
) == p.charAt(j -
1
) || p.charAt(j -
1
) ==
'?'
) {
dp[i][j] = dp[i -
1
][j -
1
];
}
}
else
{
dp[i][j] = dp[i -
1
][j -
1
] || dp[i][j -
1
] || dp[i -
1
][j];
}
}
}
return
dp[s.length()][p.length()];
}
p[j-1] == s[i-1] || p[j-1] == '?':dp[i][j] = dp[i-1][j-1]
p[j-1] == '*':
1. 匹配0个字符:dp[i][j] = dp[i][j-1]
2. 匹配1个字符:dp[i][j] = dp[i-1][j-1]
3. 匹配多个字符:dp[i][j] = dp[i-1][j]
先用二维数组写,发现会Memory Limit Exceeded
https://discuss.leetcode.com/topic/10794/my-java-dp-solution
https://discuss.leetcode.com/topic/10794/my-java-dp-solution
At first I cannot pass the the long 'aaa...' test case. Then I add more check and pass it.
public boolean isMatch(String s, String p) {
int m = s.length(), n = p.length();
int count = 0;
for (int i = 0; i < n; i++) {
if (p.charAt(i) == '*') count++;
}
if (count==0 && m != n) return false;
else if (n - count > m) return false;
boolean[] match = new boolean[m+1];
match[0] = true;
for (int i = 0; i < m; i++) {
match[i+1] = false;
}
for (int i = 0; i < n; i++) {
if (p.charAt(i) == '*') {
for (int j = 0; j < m; j++) {
match[j+1] = match[j] || match[j+1];
}
} else {
for (int j = m-1; j >= 0; j--) {
match[j+1] = (p.charAt(i) == '?' || p.charAt(i) == s.charAt(j)) && match[j];
}
match[0] = false;
}
}
return match[m];
}
https://blog.csdn.net/aximi/article/details/83898167
https://leetcode.com/problems/wildcard-matching/discuss/17812/My-java-DP-solution-using-2D-table
public boolean isMatch(String s, String p) {
boolean[][] match=new boolean[s.length()+1][p.length()+1];
match[s.length()][p.length()]=true;
for(int i=p.length()-1;i>=0;i--){
if(p.charAt(i)!='*')
break;
else
match[s.length()][i]=true;
}
for(int i=s.length()-1;i>=0;i--){
for(int j=p.length()-1;j>=0;j--){
if(s.charAt(i)==p.charAt(j)||p.charAt(j)=='?')
match[i][j]=match[i+1][j+1];
else if(p.charAt(j)=='*')
match[i][j]=match[i+1][j]||match[i][j+1];
else
match[i][j]=false;
}
}
return match[0][0];
}
public boolean isMatch(String s, String p) {
int m = s.length(), n = p.length();
char[] ws = s.toCharArray();
char[] wp = p.toCharArray();
boolean[][] dp = new boolean[m+1][n+1];
dp[0][0] = true;
for (int j = 1; j <= n; j++)
dp[0][j] = dp[0][j-1] && wp[j-1] == '*';
for (int i = 1; i <= m; i++)
dp[i][0] = false;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (wp[j-1] == '?' || ws[i-1] == wp[j-1])
dp[i][j] = dp[i-1][j-1];
else if (wp[j-1] == '*')
dp[i][j] = dp[i-1][j] || dp[i][j-1];
}
}
return dp[m][n];
}
X. O(n) space
https://leetcode.com/problems/wildcard-matching/discuss/17871/O(n)-space-DP-using-two-1-d-array-to-rotate.
public boolean isMatch(String s, String p) {
if(p.isEmpty())
return s.isEmpty();
/*init array*/
boolean[] d = new boolean[s.length() +1];
boolean[] next = new boolean[s.length() +1];
d[0] = true;
for(char c: p.toCharArray()){
next[0] = c == '*' ? d[0] : false; // init first element
for(int i = 1; i <= s.length(); i ++){
if(c == '?'){
next[i] = d[i-1];
}
else if (c == '*'){
next[i] = d[i] || next[i-1];
}
else { // normal char
next[i] = d[i-1] && (c == s.charAt(i-1));
}
}
// rotate forward, dont have to clean up.
boolean[] tmp = d;
d = next;
next = tmp;
}
return d[s.length()];
}
https://leetcode.com/problems/wildcard-matching/discuss/17893/Java-DP-O(mn)-time-O(n)-space-with-rolling-array-trick public boolean isMatch(String s, String p) {
int m = s.length(), n = p.length();
boolean[][] match = new boolean[2][n + 1];
match[0][0] = true;
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
if (j == 0) { // initialized first column
match[i % 2][j] = i == 0;
continue;
}
if (p.charAt(j - 1) == '*') {
match[i % 2][j] = (i > 0 && match[(i - 1) % 2][j]) || match[i % 2][j - 1];
} else {
match[i % 2][j] = i > 0 && (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '?') && match[(i - 1) % 2][j - 1];
}
}
}
return match[m % 2][n];
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class Solution { public: bool isMatch(const char *s, const char *p) { int m = strlen(s), n = strlen(p); vector<bool> dp(m+1, false); for(int i=0; i<m; i++) { bool diag = dp[0]; dp[0] = i==0 ? true : false; for(int j=1; j<n; j++) { int temp = dp[j]; if(p[j-1]==s[i-1] || p[j-1]=='?') dp[j] = i>0 && diag; else if(p[j-1]=='*') dp[j] = dp[j-1] || (i>0 && (diag || dp[j])); diag = temp; } } return dp[n]; } }; |
public boolean isMatch(String s, String p) {
int m=s.length();
int n=p.length();
boolean[] dp=new boolean[n+1];
for (int i=0; i<=m; i++) {
boolean pre=false;
for (int j=0; j<=n; j++) {
boolean temp=dp[j];
if (i==0) {
if (j==0) dp[j]=true;
else if (p.charAt(j-1)=='*') dp[j]=dp[j-1];
else dp[j]=false;
}
else if (j==0) dp[j]=false;
else if (p.charAt(j-1)=='?') dp[j]=pre;
else if (p.charAt(j-1)=='*') dp[j]=dp[j] || dp[j-1];
else if (s.charAt(i-1)==p.charAt(j-1)) dp[j]=pre;
else dp[j]=false;
pre=temp;
}
}
return dp[n];
}
TODO:https://discuss.leetcode.com/topic/11392/my-java-dp-solutionpublic boolean isMatch(String s, String p) {
if (p.replace("*", "").length() > s.length())
return false;
boolean[] d = new boolean[s.length() + 1];
d[0] = true;
for (int i = 1; i < s.length(); ++i) {
d[i] = false;
}
for (int i = 1; i <= p.length(); ++i) {
char pchar = p.charAt(i - 1);
if (pchar == '*') {
for (int j = 1; j <= s.length(); ++j) {
d[j] = d[j - 1] || d[j];
}
}
else {
for (int j = s.length(); j >= 1; --j) {
d[j] = d[j - 1] && (pchar == '?' || pchar == s.charAt(j - 1));
}
}
d[0] = d[0] && pchar == '*';
}
return d[s.length()];
}
X. Space Optimization:http://likesky3.iteye.com/blog/2217581
- // Method1: Time: O(m * n), Space: O(n),两个一维数组
- // dp[i][j] : p的前i个字符能否匹配s的前j个字符
- public boolean isMatch1(String s, String p) {
- if (p == null || s == null)
- return false;
- int lengthS = s.length();
- int lengthP = p.length();
- if (lengthS > 300 && p.charAt(0) == '*' && p.charAt(lengthP - 1) == '*')
- return false;
- boolean[][] dp = new boolean[2][lengthS + 1];
- dp[0][0] = true;
- int lastRow = 1;
- int currRow = 0;
- for (int i = 1; i <= lengthP; i++) {
- lastRow = currRow;
- currRow = 1 - lastRow;
- char charP = p.charAt(i - 1);
- dp[currRow][0] = dp[lastRow][0] && charP == '*';
- for (int j = 1; j <= lengthS; j++) {
- char charS = s.charAt(j - 1);
- if (charP == charS || charP == '?') {
- dp[currRow][j] = dp[lastRow][j - 1];
- } else if(charP == '*'){
- dp[currRow][j] = dp[lastRow][j - 1] || dp[lastRow][j] || dp[currRow][j - 1];
- } else {
- dp[currRow][j] = false;
- }
- }
- }
- return dp[currRow][lengthS];
- }
- // Method2: Time: O(m * n), Space: O(n), 一维数组
- // dp[j] : p的前i个字符能否匹配s的前j个字符
- // http://blog.csdn.net/linhuanmars/article/details/21198049
- public boolean isMatch2(String s, String p) {
- if (p == null || s == null)
- return false;
- int lengthS = s.length();
- int lengthP = p.length();
- if (lengthS > 300 && p.charAt(0) == '*' && p.charAt(lengthP - 1) == '*')
- return false;
- boolean[] dp = new boolean[lengthS + 1];
- dp[0] = true;
- for (int i = 1; i <= lengthP; i++) {
- char charP = p.charAt(i - 1);
- if (charP != '*') {
- for (int j = lengthS; j >= 1; j--) {
- dp[j] = dp[j - 1] && (charP == s.charAt(j - 1) || charP == '?');
- }
- } else {
- int j = 0;
- while (j <= lengthS && !dp[j])
- j++;
- while (j <= lengthS) {
- dp[j++] = true;
- }
- }
- dp[0] = dp[0] && charP == '*';
- }
- return dp[lengthS];
- }
https://discuss.leetcode.com/topic/10794/my-java-dp-solution
public boolean isMatch(String s, String p) {
int m = s.length(), n = p.length();
int count = 0;
for (int i = 0; i < n; i++) {
if (p.charAt(i) == '*') count++;
}
if (count==0 && m != n) return false;
else if (n - count > m) return false;
boolean[] match = new boolean[m+1];
match[0] = true;
for (int i = 0; i < m; i++) {
match[i+1] = false;
}
for (int i = 0; i < n; i++) {
if (p.charAt(i) == '*') {
for (int j = 0; j < m; j++) {
match[j+1] = match[j] || match[j+1];
}
} else {
for (int j = m-1; j >= 0; j--) {
match[j+1] = (p.charAt(i) == '?' || p.charAt(i) == s.charAt(j)) && match[j];
}
match[0] = false;
}
}
return match[m];
}
http://blog.csdn.net/linhuanmars/article/details/21198049
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()]; }http://www.jiuzhang.com/solutions/wildcard-matching/
http://shmilyaw-hotmail-com.iteye.com/blog/2154716
在不考虑"*"的情况下,我们的方法大致结构如下:
- boolean isMatch(String s, String p) {
- int l = 0, r = 0;
- while(l < s.length() && r < p.length()) {
- if(s.charAt(l) == p.charAt(r) || p.charAt(r) == '?') {
- l++;
- r++
- } else return false;
- }
- if(l < s.length() || r < p.length()) return false;
- return true;
- }
1.假设碰到*号时s串的位置是l, p串的位置是r,则将r+1作为*号后续进行比较的起点。
2.从l开始与r+1位置的元素进行比较,如果相同,再按照常规匹配的方式往后面继续比。否则l跳到前面和r+1开始比较那个位置的后面一个。同时p这边的元素也要归位到r+1
所以,到了这一步,我们就知道。当遇到*号的时候,先记录一下l所在的位置和r+1所在的位置。每次匹配比较不对了就回退。假设记录l开始比较位置的元素为match。在每次匹配失败后match就要加1,表示下一个开始比较的起点。
3.既然前面匹配的情况讨论了。对于不匹配的情况呢?假设我们碰到一些不匹配的了。如果前面根本就没有*号,这就是正常的字符串比较不匹配的情况,我们直接就返回匹配不成功了。
4.还有一个问题,这算是比较隐蔽一点的情况。因为我们这边是一碰到*号就一直不停的去比较,如果p串中间有多个*号呢?我们可能已经比较到前面s串的结尾了,但是p串后面可能还有一部分字符。这个时候我们也需要进行判断。如果剩下的是*号,则相当于*号匹配了0个元素,否则表示匹配错误。
- public boolean isMatch(String str, String pattern) {
- int s = 0, p = 0, match = 0, starIdx = -1;
- while (s < str.length()){
- // advancing both pointers
- if (p < pattern.length() && (pattern.charAt(p) == '?' || str.charAt(s) == pattern.charAt(p))){
- s++;
- p++;
- }
- // * found, only advancing pattern pointer
- else if (p < pattern.length() && pattern.charAt(p) == '*'){
- starIdx = p;
- match = s;
- p++;
- }
- // last pattern pointer was *, advancing string pointer
- else if (starIdx != -1){
- p = starIdx + 1;
- match++;
- s = match;
- }
- //current pattern pointer is not star, last patter pointer was not *
- //characters do not match
- else return false;
- }
- //check for remaining characters in pattern
- while (p < pattern.length() && pattern.charAt(p) == '*')
- p++;
- return p == pattern.length();
- }
它的时间复杂度的话会发现这是一个指数级别的.
- boolean isMatch(String s, String p, int l, int r) {
- if(r == p.length()) return l == s.length();
- if(p.charAt(r) == '*') {
- while(p.charAt(r) == '*') r++; // Move the index at p to a non-start char.
- while(l < s.length()) {
- if(isMatch(s, p, l, r)) return true; // Find one match, return true.
- l++; // Try the next one.
- }
- return isMatch(s, p, l, r);
- } else if(l < s.length() && (p.charAt(j) == '?' || s.charAt(l) == p.charAt(r)))
- return isMatch(s, p, l + 1, r + 1);
- return false;
- }
假设我们用F(i, j)表示串s[0...i], p[0...j]这两个串分别到i和j位置它们是否匹配的函数。那么,我们可以得到这样的一个递推关系:
F[i, j] = F[i-1, j-1] &&(s[i] == p[j] || p[j] == '?') (假设此时p[j] != '*')
F[i, j] = or F[i-1, j-1] for i = 0, ... i-1, 如果p[j] = '*'。这里表示我们从前面开始找到一个对应j-1时为true的情况,表示对应i-1的时候s串匹配的位置,则从这个位置后面所有的位置都是true。
所以按照前面的思路,我们可以定义一个二维数组boolean[][] dp = new boolean[s.length()][p.length()]
这种实现方式相对来说性能有了很大的提升,它的时间复杂度为o(m * n)。空间的复杂度也是O(m * n)。实际上,我们还可以对代码做进一步的改进。因为空间计算每次只是用到前一次计算的结果,我们可以将空间的复杂度降低到O(m)
- boolean isMatch(String s, String p) {
- if(p.length() == 0) return s.length() == 0;
- int m = s.length();
- int n = p.length();
- boolean[][] dp = new boolean[m + 1][n + 1];
- dp[0][0] = true;
- for(int j = 0; j < n; j++) {
- if(p.charAt(j) != '*') {
- for(int i = 0; i < m; i++) {
- dp[i + 1][j + 1] = dp[i][j] && (s.charAt(i) == p.charAt(j) || p.charAt(j) == '?');
- }
- } else{
- int i = 0;
- while(i < s.length() && !dp[i][j]) i++;
- for(; i < m; i++) dp[i][j] = true;
- }
- }
- return dp[m][n];
- }
对于字符串的模糊匹配问题看起来很简单,实际上牵涉到非常复杂的判断条件。
结合不同的场景,最关键的就是要找到它们的递归关系,
然后进行动态规划方法的优化。
- boolean isMatch(String s, String p) {
- if(p.length() == 0) return s.length() == 0;
- int m = s.length();
- int n = p.length();
- boolean[][] dp = new boolean[m + 1][n + 1];
- dp[0][0] = true;
- for(int j = 0; j < n; j++) {
- if(p.charAt(j) != '*') {
- for(int i = 0; i < m; i++) {
- dp[i + 1][j + 1] = dp[i][j] && (s.charAt(i) == p.charAt(j) || p.charAt(j) == '?');
- }
- } else{
- int i = 0;
- while(i < s.length() && !dp[i][j]) i++;
- for(; i < m; i++) dp[i][j] = true;
- }
- }
- return dp[m][n];
- }
int
i =
0
;
int
j =
0
;
int
star = -
1
;
int
mark = -
1
;
while
(i < s.length()) {
if
(j < p.length()
&& (p.charAt(j) ==
'?'
|| p.charAt(j) == s.charAt(i))) {
++i;
++j;
}
else
if
(j < p.length() && p.charAt(j) ==
'*'
) {
star = j;
j++;
mark = i;
//这一步是关键,匹配s中当前字符与p中‘*’后面的字符,如果匹配,则在第一个if中处理,如果不匹配,则继续比较s中的下一个字符。
}
else
if
(star != -
1
) {
j = star +
1
;
i = ++mark;
}
else
{
return
false
;
}
}
//最后在此处处理多余的‘*’,因为多个‘*’和一个‘*’意义相同。
while
(j < p.length() && p.charAt(j) ==
'*'
) {
++j;
}
return
j == p.length();
}
bool
isMatch(
const
char
*s,
const
char
*p) {
char
cs = *s;
char
cp = *p;
if
(cp ==
'\0'
) {
return
cs == cp;
}
else
if
(cp ==
'?'
) {
if
(cs ==
'\0'
)
return
false
;
return
isMatch(s + 1,p + 1);
}
else
if
(cp ==
'*'
) {
const
char
*st = s;
for
(; *st !=
'\0'
; ++st) {
if
(isMatch(st, p+1))
return
true
;
}
return
isMatch(st,p+1);
}
else
if
(cp != cs)
return
false
;
return
isMatch(s + 1,p + 1);
}
http://zjalgorithm.blogspot.com/2014/11/leetcode-java-wildcard-matching.html
Recursive Version:
public
static
boolean isMatch(
String
s,
String
p) {
// base
if
(s.length()==
0
) {
if
(p.length()==
0
)
return
true
;
for
(int i=
0
; i<p.length(); i++) {
// for consecutive '*'
if
(p.charAt(i) !=
'*'
)
return
false
;
}
return
true
;
// only all * match empty
}
if
(p.length() ==
0
)
return
false
;
if
(p.charAt(
0
) ==
'?'
|| p.charAt(
0
) == s.charAt(
0
)) {
return
isMatch(s.substring(
1
), p.substring(
1
));
}
if
(p.charAt(
0
) ==
'*'
) {
int star =
0
;
// eliminate consecutive '*'
while
(star+
1
< p.length() && p.charAt(star+
1
) ==
'*'
) {
// !!! star+1 < p.length() is must
star++;
}
return
isMatch(s, p.substring(star+
1
)) || isMatch(s.substring(
1
), p.substring(star));
}
return
false
;
}
http://www.cnblogs.com/yuzhangcmu/p/4116153.html
动态规划:输入两个字符串s,p(p包含通配符,用p去匹配s),用flag[i][j]表示字符串p从0到i的的子字符串能否匹配s从0到j的子字符串,我们可以得到如下递推公式:
如果p.charAt(i)==s.charAt(j)或则p.charAt(i)=='?',相当于将最后一个字符匹配掉,所以
flag[i][j]=flag[i-1][j-1];
如果p.charAt(i)=='*','*'可以选择匹配0个字符,此时flag[i][j]=flag[i-1][j];可以选择匹配1个字符,此时flag[i][j]=flag[i-1][j-1];……所以,
flag[i][j]=flag[i-1][j]||flag[i-1][j-1]||……||flag[i-1][0]。
但是上面的公式可以化简,当p.charAt(i)=='*'时,有
flag[i][j-1]=flag[i-1][j-1]||flag[i-1][j-2]||……||flag[i-1][0]
所以 flag[i][j]==flag[i-1][j]||flag[i][j-1],所以综合递推公式如下:
空间复杂度?
由上面的公式可以看出,flag[i][j]只与第i行和第i-1行相关,所以只需要开辟两个一维数组即可。空间复杂度是O(n),时间复杂度是O(mn)。
http://codeganker.blogspot.com/2014/03/wildcard-matching-leetcode.html
DP: SPace:O(n)(1)p[j]不是'*'。情况比较简单,只要判断如果当前s的i和p的j上的字符一样(如果有p在j上的字符是'?',也是相同),并且res[i]==true,则更新res[i+1]为true,否则res[i+1]=false;
(2)p[j]是'*'。因为'*'可以匹配任意字符串,所以在前面的res[i]只要有true,那么剩下的 res[i+1], res[i+2],...,res[s.length()]就都是true了。
算法的时间复杂度因为是两层循环,所以是O(m*n), 而空间复杂度只用一个一维数组,所以是O(n)
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()]; }
X. DFS
其实这道题也可以使用递归来做,因为子串或者子数组这种形式,天然适合利用递归来做。但是愣了吧唧的递归跟暴力搜索并没有啥太大的区别,很容易被OJ毙掉,比如评论区六楼的那个naive的递归,其实完全是按照题目要求来的。首先判断s串,若为空,那么再看p串,若p为空,则为true,或者跳过星号,继续调用递归。若s串不为空,且p串为空,则直接false。若s串和p串均不为空,那么进行第一个字符的匹配,若相等,或者p[0]是问号,则跳过首字符,对后面的子串调用递归。若p[0] 是星号,那么先尝试跳过s串的首字符,调用递归,若递归返回true,则当前返回true。否则尝试跳过p串的首字符,调用递归,若递归返回true,则当前返回true。但是很不幸,内存超出限制了MLE,那么博主做了个简单的优化,跳过了连续的星号,参见评论区七楼的代码,但是这次时间超出了限制TLE。博主想是不是取子串substr()操作太费时间,且调用递归的适合s串和p串又分别建立了副本,才导致的TLE。于是想着用坐标变量来代替取子串,并且递归函数调用的s串和p串都加上引用,代码参见评论区八楼,但尼玛还是跪了,OJ大佬,刀下留人啊。最后还是在论坛上找到了一个使用了神奇的剪枝的方法,这种解法的递归函数返回类型不是bool型,而是整型,有三种不同的状态,返回0表示匹配到了s串的末尾,但是未匹配成功;返回1表示未匹配到s串的末尾就失败了;返回2表示成功匹配。那么只有返回值大于1,才表示成功匹配。至于为何失败的情况要分类,就是为了进行剪枝。在递归函数中,若s串和p串都匹配完成了,返回状态2。若s串匹配完成了,但p串但当前字符不是星号,返回状态0。若s串未匹配完,p串匹配完了,返回状态1。若s串和p串均为匹配完,且当前字符成功匹配的话,对下一个位置调用递归。否则若p串当前字符是星号,那么我们首先跳过连续的星号。然后我们分别让星号匹配空串,一个字符,两个字符,....,直到匹配完整个s串,对每种情况分别调用递归函数,接下来就是最大的亮点了,也是最有用的剪枝,当前返回值为状态0或者2的时候,返回,否则继续遍历。如果我们仅仅是状态2的时候才返回,就像评论区八楼的代码,会有大量的重复计算,因为当返回值为状态0的时候,已经没有继续循环下去的必要了,非常重要的一刀剪枝,参见代码如下:
bool isMatch(string s, string p) { return helper(s, p, 0, 0) > 1; } int helper(string& s, string& p, int i, int j) { if (i == s.size() && j == p.size()) return 2; if (i == s.size() && p[j] != '*') return 0; if (j == p.size()) return 1; if (s[i] == p[j] || p[j] == '?') { return helper(s, p, i + 1, j + 1); } if (p[j] == '*') { if (j + 1 < p.size() && p[j + 1] == '*') { return helper(s, p, i, j + 1); } for (int k = 0; k <= (int)s.size() - i; ++k) { int res = helper(s, p, i + k, j + 1); if (res == 0 || res == 2) return res; } } return 1; }
http://www.voidcn.com/article/p-ehnxhspt-gt.html
bool isMatch(string s, string p) {
if(p.empty()){
return s.empty();
}
if(p[0]=='*'){
int i=1;
while(p[i]=='*'){
i++;
}
p = p.substr(i);
if(p==""){
return true;
}
for(int j=0;j<s.length();j++){
if(isMatch(s.substr(j), p)){
return true;
}
}
return false;
}else{
return (p[0]=='?'|| p[0]==s[0])&& !s.empty()&&isMatch(s.substr(1),p.substr(1));
}
}