0%

10# Regular Expression Matching

弱鸡进城纪实。

今天遇到的一道正则表达式匹配问题的题,题目大意是这样的:

给定两个字符串s和p,其中s是匹配串,p是模式串,p中定义了两个匹配符:

  • ‘.’:通配符,可以指代任何字符(不含’*’)。
  • ‘*‘:该字符前的一个字符可能出现任意次,该字符包括’.’,出现次数可能为零。

问题当然就是判定给定的s与p是否匹配了。

OK,说实话,我一开始连这个问题都没看明白,后来翻别人的解法之后才明白过来……英文不好真是……哎……

这个问题的最难点就在于‘*’的处理。’*’前面的字符可能在匹配串中出现0次1次2次……这么多情况该怎么去处理?这些情况都有可能成功匹配,该怎么去简洁的处理这个问题?

递归。

递归最适合的就是处理这种需要退回栈的问题,从几个基本问题出发,在递归中不断地对分支进行处理。那么具体到这道题,有些什么基本情况呢?

  • 模式串p的第二个字符不是’*‘,那么只需要考察模式串和匹配串第一个字符是否相同,若相同,则递归的处理从下一个字符开始的s和p。
  • 模式串p的第二个字符是’*‘,那么对于’*‘前的字符i,对于每个可能的匹配次数进行匹配后再依次进行递归。

整个思路很清晰,也很有效率。下面附上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public boolean isMatch(String s, String p) {

//模式串p是空字符串时,只需要考察匹配串s是不是也为空。
if (p.length() == 0) return s.length() == 0;

//模式串p的长度为1,或者第二个字符不是'*'时,只需要考察模式串的第一个字符和匹配串的第一个字符是否相同。
if (p.length() == 1 || p.charAt(1) != '*') {

//如果此时匹配串已为空或者p和s的第一个字符不相同,则返回错误。
if (s.length() < 1 || (p.charAt(0) != '.' && s.charAt(0) != p.charAt(0))) return false;

//否则考察从下一个字符开始的s和p是否匹配。
return isMatch(s.substring(1), p.substring(1));
} else {

//此时模式串的第二个字符是'*',这种情况下,我们假定'*'前的字符为i,并且匹配串s中,前k个字符是i(k可能为零,模式串中的i可能是通配符)
//按照规则,i可能会匹配匹配串s中从第一个字符起的0到k个i,那么,对于这k种情况,我们应该分别递归的调用这个函数进行考察。
int len = s.length();
int i = -1;
while (i < len && (i < 0 || p.charAt(0) == '.' || p.charAt(0) == s.charAt(i))) {
if (isMatch(s.substring(i + 1), p.substring(2))) return true;
i++;
}

//当所有过程被执行完毕还是没有返回true时,两个字符串不匹配。
return false;
}
}