题意整理
- 用模式串来检验整个匹配串。
- 模式串中的'.'匹配任何字符,'*'表示它前面的字符可以出现任意次(即0到无穷次)。
方法一(记忆化递归)
1.解题思路
- 递归终止条件:当模式串走完的时候,递归终止,如果此时,原串也走完了,则匹配成功;如果没走完,则匹配失败。
- 每一层递归从上一层获取什么:当模式串p的当前字符的后一个为'*'时,我们将'*'所在位置和前一个位置连在一起考虑,不管是否满足原串与模式串匹配,都可以匹配0个字符,即s的游标不动,p的游标往后移2格;如果满足原串与模式串匹配,则匹配0个或大于0的任意个字符,只需s的游标后移1格,p的游标不动。当模式串p的当前字符后一个不为'*'时,只需看原串与模式串是否匹配,若匹配,一起往后走一个格子;若不匹配,返回false。
- 每一层递归返回什么:根据实际的情况,将原串s和模式串p后移若干格子。
由于直接递归会走很多弯路,我们可以用一个记忆数组来存储递归过程中,已经确定过的情况,然后进行剪枝,如果发现记忆化数组中存储了已经出现过的情况,直接返回。
2.代码实现
普通递归:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @param pattern string字符串
* @return bool布尔型
*/
public boolean match (String str, String pattern) {
return match(str.toCharArray(),0,pattern.toCharArray(),0);
}
private boolean match(char[] s,int si,char[] p,int pi){
////如果模式串走完了,原串没走完,则返回false;走完了,则返回true
if(pi>=p.length) return si>=s.length;
if(pi+1<p.length&&p[pi+1]=='*'){
//如果满足'*'前一个字符与原串匹配
if(si<s.length&&(s[si]==p[pi]||p[pi]=='.')){
//<*和前一个字符>匹配<原串字符>1到无穷次或者0次
return match(s,si+1,p,pi)
||match(s,si,p,pi+2);
}
//<*和前一个字符>匹配<原串字符>0次
return match(s,si,p,pi+2);
}
//<模式串字符>匹配<原串字符>1次
if(si<s.length&&(s[si]==p[pi]||p[pi]=='.')){
return match(s,si+1,p,pi+1);
}
return false;
}
}记忆化递归:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @param pattern string字符串
* @return bool布尔型
*/
int[][] memo;
public boolean match (String str, String pattern) {
//初始化记忆数组
memo=new int[str.length()+1][pattern.length()+1];
return match(str.toCharArray(),0,pattern.toCharArray(),0);
}
private boolean match(char[] s,int si,char[] p,int pi){
//如果模式串走完了,原串没走完,则返回false;走完了,则返回true
if(pi>=p.length) return si>=s.length;
//如果之前某种情况被memo记录下来,并且是对的,则返回true
if(memo[si][pi]!=0) return memo[si][pi]==1;
if(pi+1<p.length&&p[pi+1]=='*'){
//如果满足'*'前一个字符与原串匹配
if(si<s.length&&(s[si]==p[pi]||p[pi]=='.')){
//<*和前一个字符>匹配<原串字符>1到无穷次或者0次
boolean f=match(s,si+1,p,pi)
||match(s,si,p,pi+2);
memo[si][pi]=f?1:-1;
return f;
}
//<*和前一个字符>匹配<原串字符>0次
boolean f=match(s,si,p,pi+2);
memo[si][pi]=f?1:-1;
return f;
}
//<模式串字符>匹配<原串字符>1次
if(si<s.length&&(s[si]==p[pi]||p[pi]=='.')){
boolean f=match(s,si+1,p,pi+1);
memo[si][pi]=f?1:-1;
return f;
}
return false;
}
} 3.复杂度分析
- 时间复杂度:最坏的情况,会遍历整个记忆数组,所以时间复杂度为
。
- 空间复杂度:需要额外
大小的记忆数组,所以空间复杂度为
。
方法二(动态规划)
1.解题思路
- 状态定义:
表示原字符串长度为n,模式串长度为m时,是否能够匹配。
- 初始化:当原串和模式串都为空时,显然能够匹配,
;当模式串为空,而原串不为空时,均为
。
- 状态转移:分模式串后一个位置是否为'*'进行讨论,为'*'时,将'*'与前一个位置合并起来进行考虑。如果后一个位置不为'*',并且当前模式串字符和原串字符匹配,则满足
;如果后一个位置为'*',
无论是否匹配,均满足dp[i][j]=dp[i][j-2] (匹配0次),如果匹配,满足(匹配1到无穷次或0次)
动图展示:
2.代码实现
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @param pattern string字符串
* @return bool布尔型
*/
public boolean match (String str, String pattern) {
int n=str.length();
int m=pattern.length();
boolean[][] dp=new boolean[n+1][m+1];
//初始化
dp[0][0]=true;
for(int i=1;i<=n;i++){
dp[i][0]=false;
}
//分模式串的后一个位置是否为*进行讨论,为*时,将*与前一个位置合并起来进行考虑
for(int i=0;i<=n;i++){
for(int j=1;j<=m;j++){
if(pattern.charAt(j-1)!='*'){
//当前模式串字符和原串字符匹配
if(i>0&&(str.charAt(i-1)==pattern.charAt(j-1)||(pattern.charAt(j-1)=='.'))){
dp[i][j]=dp[i-1][j-1];
}
}
else{
if(j>=2){
//不管是否匹配,都可以将当前字符绑定上*匹配原串字符0次
dp[i][j]=dp[i][j-2];
//当前模式串字符和原串字符匹配
if(i>0&&(str.charAt(i-1)==pattern.charAt(j-2)||(pattern.charAt(j-2)=='.'))){
dp[i][j]=dp[i-1][j]||dp[i][j-2];
}
}
}
}
}
return dp[n][m];
}
} 3.复杂度分析
- 时间复杂度:需要计算所有的状态,总共有
种状态,所以时间复杂度为
。
- 空间复杂度:需要额外
大小的dp数组,所以空间复杂度为
。

京公网安备 11010502036488号