题目:通配符匹配
难度:hard
题目内容:
Given an input string (s
) and a pattern (p
), 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).
Note:
s
could be empty and contains only lowercase lettersa-z
.p
could be empty and contains only lowercase lettersa-z
, and characters like?
or*
.
翻译:给定一个输入字符串(s)和一个模版(p),实现通配符模式匹配,并支持“?””和“*”。
”?匹配任何一个字符。
“匹配任何字符序列(包括空序列)。
匹配应该覆盖整个输入字符串(而不是部分)。
注意:
s可以是空的,并且只包含小写字母a-z。
p可以是空的,只包含小写字母a-z,还有?或*。
我的思路:此题和第10题很像:Regular Expression Matching,但是10题里是' . '和 ' * ',这里是' ? ' 和 ‘ * ’,
' . '和 ' ? ' 意义相对应,但是 ' * ' 表示意义不同,第10题是只代表个数,本题是代表任何序列,于是把第十题代码修改下:
1、first_match要加上 pattern.charAt(0) == '*' 的情况;
2、后面分两种互斥情况A、B:
A:以 * 匹配:(此时又分两种相容情况)
a1:*没有匹配任何的字符
a2:*匹配上此字符
A情况的结果为 a1 || a2
B:没*:
当前匹配结果 && 下一个元素和下一个pattern的匹配结果。
我的代码:
1 public boolean isMatch(String text, String pattern) { 2 if (pattern.isEmpty()) return text.isEmpty(); 3 boolean first_match = (!text.isEmpty() && 4 (pattern.charAt(0) == text.charAt(0) || pattern.charAt(0) == '?' || pattern.charAt(0) == '*')); 5 6 if (pattern.charAt(0) == '*'){ 7 return (isMatch(text, pattern.substring(1)) || 8 (isMatch(text.substring(1), pattern))); 9 } else { 10 return first_match && isMatch(text.substring(1), pattern.substring(1)); 11 } 12 }
结果:1614 / 1808 test cases passed.
Last executed input: "aaabbbaabaaaaababaabaaabbabbbbbbbbaabababbabbbaaaaba" "a*******b"
------Time Limit Exceeded------
看来那个方法是可行的,可能是因为用了递归,中间太多***导致子递归太多,所以运行太慢。
答案代码:
1 public boolean isMatch(String s, String p) { 2 int m = s.length(), n = p.length(); 3 char[] sc = s.toCharArray(); 4 char[] pc = p.toCharArray(); 5 boolean[][] dp = new boolean[m + 1][n + 1]; 6 dp[0][0] = true; 7 for(int j = 1; j < n + 1; j++){ 8 if(pc[j - 1] == '*') dp[0][j] = dp[0][j - 1]; 9 } 10 11 for(int i = 1; i < m + 1; i++){ 12 for(int j = 1; j < n + 1; j++){ 13 if(pc[j - 1] == sc[i - 1] || pc[j - 1] == '?'){ 14 dp[i][j] = dp[i - 1][j - 1]; 15 } else if(pc[j - 1] == '*'){ 16 dp[i][j] = dp[i - 1][j] || dp[i][j - 1]; 17 } else { 18 dp[i][j] = false; 19 } 20 } 21 } 22 return dp[m][n]; 23 }
答案复杂度:O(M*N)
答案思路:使用一个二维数组对每个点的匹配状态进行记录,后面点的匹配状态取决于当前匹配和之前匹配,所以最后一个点的匹配状态就是整个匹配结果。
数组的行列分别为pattern,str
算法主要分两部分:
1、给第0行和第0列赋值:
第0列——除了第一个为true以外都为默认值false;
第0行——如果pattern某个位置为*,则此时对应第0行的值等于前面一个的值,否则取默认值false。
2、从第第1行第1列的元素开始:
如果当前str值与pattern的值直接匹配上(或者匹配?)则二维数组此值直接取上一个“元素”的匹配结果:【i-1】【j-1】
如果当前str值与pattern的值是靠‘’*‘’才匹配上,则二维数组此值等于
(*没有匹配上此字符 || *匹配上此字符)==(这个元素和上一个pattern结果 || 这一个pattern和上一个元素的结果)
以上两者都不是,则取默认值false
eg. pattern = *a**a str = bbaa
bp[][]= ' ' * a * * a
'' [true, true, false, false, false, false]
b [false, true, false, false, false, false]
b [false, true, false, false, false, false]
a [false, true, true, true, true, false]
a [false, true, true, true, true, true]
结论:第二种(答案)方法虽然不错,但是理解起来太麻烦,不过也很好记,记住思路也行。。。
在一般的面试或者笔试中使用第一种(我写的)方法足以应对。