题目描述
给定一个字符串 s 和一个字符规律 p,实现一个支持 '.' 和 '*' 的正则表达式匹配。
1. '.' 匹配任意单个字符
2. '*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
1) 0 <= s.length <= 20
2) 0 <= p.length <= 30
3) s 可能为空,且只包含从 a-z 的小写字母。
4) p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
5) 保证每次出现字符 * 时,前面都匹配到有效的字符
解题思路
用'.'和'*'来匹配字符串有很大的灵活性,因为.*可以匹配任意的字符串,这里基于动态规划来实现匹配
首先定义一个二维数组dp[p.length][s.length + 1],dp[i][j]表示p[0~i]的子字符串可以匹配s[0~ j-1]子字符串,因为.*可以匹配空字符串,因此dp[i][0]表示p[0~i]子字符串可以匹配空字符串
接下来需要找出状态之间的转移关系。
1) 首先看i=0的情况,即用p的第一个字符去匹配s,由于字符*前面一定能匹配到有效字符,故p的第一个字符只能为字母或者'.',由于单个字符是不能匹配空字符串的,故dp[0][0]=false
接着用p的第一个字符去匹配s的其它字符,由于p[0]只能为字母或'.',当p[0]为字母时若和s的第一个字母相同,则dp[0][1]=true; 当p[0]为'.'时可以匹配任意字母,则dp[0][1]=true
p的第一个字符只能和s的第一个字符进行匹配,故当i=0时,只需匹配第一个字符即可
2) 再来看j=0的情况,即用p去匹配空字符串,先看几个例子
a) a*b* 可以匹配空字符串(a和b均认为出现0次)
b) a*b 不能匹配空字符串(a可以认为出现0次,但b不能)
c) a*bc* 不能匹配空字符串(a和c可以认为出现0次,但b不能)
通过例子可以看出,p能匹配空字符串的必要条件时最后一个字符必须是'*', 若p[i]能匹配空字符串,且p[i+2]='*',则p[i+2]仍能匹配空字符串,因此可以得到状态转移的关系:
若dp[i][0]=true, 且p[i+2]='*',则dp[i+2][0]=true
3) 再来看其它情况,若dp[i][j]=true,且p[i+1]=s[j+1]或者p[i+1]='.',则表明p[i+1]仍然可以匹配s[j+1],即dp[i+1][j+1]=true
4) 最后来看p[i]='*'的情况,这也是最复杂的情况,看几个例子
a) ab可以匹配ab,则ab.*或abc*也可以匹配ab,由此可以得出转移状态: 若dp[i][j]=true,且p[i+2]='*',则dp[i+2][j]=true
b) ab*可以匹配a,则ab*可以匹配ab、abb等,由此可以得出转移状态: 若dp[i][j]=true,且p[i]='*',若p[i-1]='.'或p[i-1]=s[j+1],则dp[i][j+1]=true
代码实现
class Solution {
public boolean isMatch(String s, String p) {
if (null == s || null == p) {
return false;
}
if (p.equals(".*")) {
return true;
}
if ("".equals(p)) {
return "".equals(s);
}
//dp[i][j]表示p的0~i位字符串可以匹配s的0~j位字符串
boolean[][] dp = new boolean[p.length()][s.length() + 1];
for (int i=0; i < p.length(); i++) {
for (int j=0; j < s.length() + 1; j++) {
//p的第一个字符只需和s的第一个字符进行比较
if (i == 0) {
if (!"".equals(s) && (p.charAt(0) == s.charAt(0) || p.charAt(0) == '.')) {
dp[0][1] = true;
}
break;
}
if (j == 0 && p.charAt(i) == '*') {
//.*在开头
if (i == 1) {
dp[i][j] = true;
} else if (dp[i-2][j]) {
//.*认为是空或匹配1个字符
dp[i][j] = true;
}
}
if ((j > 0 && dp[i-1][j-1]) && !"".equals(s) &&
(p.charAt(i) == s.charAt(j - 1) || p.charAt(i) == '.')) {
dp[i][j] = true;
} else if (p.charAt(i) == '*') {
if (i >= 2 && dp[i-2][j]) {
//ab可以匹配ab,则ab.*或abc*也可以匹配ab
dp[i][j] = true;
}
//ab*可以匹配a,则ab*可以匹配ab、abb
if (j > 0 && dp[i][j-1] && (p.charAt(i-1) == '.' || p.charAt(i-1) == s.charAt(j - 1))) {
dp[i][j] = true;
}
}
}
}
return dp[p.length() - 1][s.length()];
}
}