题目
请实现一个函数用来匹配包含’. ‘和’*‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。
思路:从s[0:1]与p[0:1]是否匹配开始判断,然后每轮增加一个字母,可分为s增加一字母,判断是否匹配;p增加一位,判断是否匹配,直至判断s[0:m]与p[0:n]是否匹配。 (设s.length()=m, p.length()=n)
这样就有m*n种状态
动态规划:
-
状态定义:m*n维dp矩阵,dp[i][j]表示s前i个字母与p前j位匹配
-
状态转移方程:为了在dp的下标与s,p下标对齐,先对s、p进行处理。s = " " + s, p = " " + p。这样s[i]就表示第i位字母
当p[j] = '*'时:以下3种请况为true则为true
1. dp[i][j-2] :p[j-1]* 取0次 如:s = a p = ab*,b*等于空
2. dp[i-1][j] && s.charAt(i) == p.charAt(j-1):让p[j-1]* 取两次或取一次
举例:s = aaa p = aa* (a*=aa) or s = aa p = aa*(a*=a)
3.(dp[i-1][j] && p.charAt(j-1) == ‘.’):与2类似,不过把a变成了’.’,’.'可以匹配任意一个字符,让p[j-1]* 取两次或取一次 。
举例:s = aaa p = a.*(.* = aa) or s = aa p = a.*(.* = a)当p[j] !=’*'时:
普通字符或’.'的匹配,匹配时,除了配对s[i]==p[j]外,还应检查s[:i]能否与p[:j]配对。
即检查dp[i-1][j-1]是否为true- dp[i-1][j-1] && (s.charAt(i) == p.charAt(j):s[i]和p[j]为相同字母,且s前i-1个字母与p前j-1位匹配
- (dp[i-1][j-1] && p.charAt(j) == ‘.’):p[j]=’.'可以配对任意字母,且s前i-1个字母与p前j-1位匹配。
-
初始状态:s和p的前面是空字符,两空字符匹配,dp[0][0]=1。
初始化首行 ,当s = " ",只有当p的偶数位为*才可以匹配成功 如a*a*(也就相当于空) -
返回值:返回dp[m-1][n-1],即s与p是否匹配成功
class Solution{
public boolean isMatch(String s,String p){
int m = s.length() + 1, n = p.length() + 1;
//使dp的下标与s,p下标对齐
s = " " + s;
p = " " + p;
boolean[][] dp = new boolean[m][n];
//s = " ",p=" ",空字符能匹配成功
dp[0][0] = true;
//初始化首行 s = " ",只有当p的偶数位为*才可以匹配成功 如a*a*
for(int j = 2;j < n; j++){
dp[0][j] = dp[0][j-2] && p.charAt(j) == '*';
}
//s前i个字符与p前j个字符是否能匹配上
for(int i = 1; i < m; i++){
for(int j = 1; j < n; j++){
if(p.charAt(j) == '*'){
//p[j-1]* 取0次 s = a p = ab*
if(dp[i][j-2])dp[i][j] = dp[i][j-2];
//让p[j-1]* 取两次或取一次 s = aaa p = aa*/a.* or s = aa p = aa*/a.*
else if(dp[i-1][j] && (s.charAt(i) == p.charAt(j-1) || p.charAt(j-1) == '.'))
dp[i][j] = true;
}
else{
//普通字符或'.'的匹配,匹配时,除了配对s[i]==p[j]外,还应检查s[:i]能否与p[:j]配对
//即检查dp[i-1][j-1]是否为true
//s[i]和p[j]为相同字母或p[j]='.'可以配对任意字母
if(dp[i-1][j-1] && (s.charAt(i) == p.charAt(j) || p.charAt(j) == '.'))dp[i][j] = true;
}
}
}
return dp[m][n];
}
}
题目来源:力扣(LeetCode)