正则表达式匹配
题目描述:请实现一个函数用来匹配包括 '.' 和 '*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '*' 表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 "aaa" 与模式 "a.a" 和 "ab*ac*a" 匹配,但是与 "aa.a" 和 "ab*a" 均不匹配。
分析:这题好难理解,先把题解放在这里。
示例 1:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:
输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 4:
输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。
示例 5:
输入:
s = "mississippi"
p = "mis*is*p*."
输出: false
首先肯定是两个指针,一个在给定字符串上动,一个在模板上动。如果匹配肯定都向后移动,但此时由于有两个特殊符号捣乱,因此比较特殊。首先分析 '.' 的情况。当我们比较两个指针指的字符时,模板的字符是 '.',那么可以让两个指针同时后移。这个情况简单,就默认相等嘛。换句话说,本题只用考虑有没有 '*' 就可以了。考虑 '*' 的相关情况情况。我们比较两个指针的字符时,如果字符串是 S 指针,模板是 P 指针指向某个字符。那么 P 的下一个字符不是 '*' 时,那么就是最普通的情况,直接都往后移一位。即 s++,p++。
分析此时有 '*' 时,注意此时,p+1 指向了一个 '*'。那么我们怎么移动指针呢。
- 方式 1:出现了 *',而且 s 和 p 指向的都不相等,就相当于模板中 '*' 前面的出现了 0 次,此时必须让 p 指针向后移动 2 位,让模板跳过 '*' 即可。(对于 abge 和 ac*mn,s 指 b,p 指 c,则让 p 指向 m,即 p+2。认为 c 没出现过)
- 方式 2:出现了 '*',而且 s 和 p 指向的相等(这里的相等可以是真正的相等,也可以是 '.' 标记的相等)。比如 abcd 和 ab*cf,其中 s 和 p 都指向了 b。此时应该 s+1 和 p+2。比如 abbcd 和 ab*cf,其中 s 和 p 都指向了 b。由于 b 出现了多次,应该不着急移动 p,所以此时 s+1 即可。比如 "cba", "cb*a*a",我也可以认为,p 指向的第 1 个 a 没出现过,即使你相等。因此此时可以 s 不动,p+2。
可以看到,方式 2 中其实又分为 3 种情况,而实际上,我们无法区分具体应该选择哪一个方式处理,所以只能将 3 个方法同时利用或运算并列起来。其中方式 2 的第 3 种处理很不容易想到,因为就算相等了,我还是认为你不相等,忽略你,是真的牛。
再次捋一遍。由于把真正的相等和 '.' 标记的相等并成一类,就只用考虑 '*'。如果不出现星号,就常规比较,常规比较肯定出现相等或者不等两类。如果出现星号,就考虑两个指针指的是否相等,不等,则 p 指针直接 +2。两个指针相等,则有 3 种情况并列处理。再在这些判断中,加入对应边界条件,就可以很容易理解并写出代码了。
PS:目前处于不理解阶段!
1 public class Solution { 2 public boolean match(char[] str, char[] pattern) { 3 if(str == null || pattern == null) { 4 return false; 5 } 6 return matchCore(str, 0, pattern, 0); 7 } 8 private boolean matchCore(char[] str, int s, char[] pattern, int p) { 9 //下面4行是递归结束标志,两个指针都指到了最后,才是匹配,否则不匹配 10 if(s == str.length && p == pattern.length) { 11 return true; 12 } 13 if(s < str.length && p == pattern.length) { 14 return false; 15 } 16 //虽然比的是P位置的,但是P后面出现*时,规则需要改变。 17 if(p + 1 < pattern.length && pattern[p + 1] == '*') { 18 //出现了*,并且s和P指向的相同,3种情况并列 19 if((s < str.length && pattern[p]=='.') || (s < str.length && pattern[p]==str[s])){ 20 return matchCore(str, s, pattern, p + 2) 21 || matchCore(str, s + 1, pattern, p) 22 || matchCore(str, s + 1, pattern, p + 2); 23 } else { //出现了*,并且s和p指向的不同,那就把*前面的字符理解出现了0次,p+2 24 return matchCore(str, s, pattern, p + 2); 25 } 26 } 27 //说明P后面不是*,那么就进行常规判断。相同就分别给指针+1 28 if (s < str.length && (pattern[p] == str[s] || pattern[p] == '.')) 29 return matchCore(str, s + 1, pattern, p + 1); 30 //p后面又不是*,也没有.给你撑腰,你还敢出现不同,那必然false 31 return false; 32 } 33 }