题目描述
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。
解法有很多,有非递归的写法,动态规划以及递归,感觉这个题属于难题了,能做出来即可,难度高还追求多种解法不是本菜鸟的风格。嗯,所以我选择在看懂递归代码后,做个总结。
代码思路:
首先肯定是两个指针,一个在给定字符串上动,一个在模板上动。如果匹配肯定都向后移动,但此时由于有两个特殊符号捣乱,因此比较特殊。
首先分析'.'的情况。当我们比较两个指针指的字符时,模板的字符是'.',那么可以让两个指针同时后移。这个情况简单,就默认相等嘛。换句话说,本题只用考虑有没有'*'就可以了。
考虑'*'的相关情况情况。我们比较两个指针的字符时,如果字符串是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种情况并列处理。再在这些判断中,加入对应边界条件,就可以很容易理解并写出代码了。
public class Solution { public boolean match(char[] str, char[] pattern) { if (str == null || pattern == null) return false; return matchCore(str, 0, pattern, 0); } private boolean matchCore(char[] str, int s, char[] pattern, int p) { //下面4行是递归结束标志,两个指针都指到了最后,才是匹配,否则不匹配 if (s == str.length && p == pattern.length) return true; if (s < str.length && p == pattern.length) return false; //虽然比的是P位置的,但是P后面出现*时,规则需要改变。 if (p + 1 < pattern.length && pattern[p + 1] == '*') { //出现了*,并且s和P指向的相同,3种情况并列 if ((s < str.length && pattern[p] == '.') || (s < str.length && pattern[p] == str[s])) { return matchCore(str, s, pattern, p + 2) || matchCore(str, s + 1, pattern, p) || matchCore(str, s + 1, pattern, p + 2); } else {//出现了*,并且s和p指向的不同,那就把*前面的字符理解出现了0次,p+2 return matchCore(str, s, pattern, p + 2); } } //说明P后面不是*,那么就进行常规判断。相同就分别给指针+1 if (s < str.length && (pattern[p] == str[s] || pattern[p] == '.')) return matchCore(str, s + 1, pattern, p + 1); //p后面又不是*,也没有.给你撑腰,你还敢出现不同,那必然false return false; } }