问题:给定匹配串s和模式串p(只包含.和*模式),确定s是否与p匹配.
例:(. 表示任一字符匹配,* 表示p前一字符0次以上匹配)
s = “aaa”, p = “a*”,匹配;
s = “ad”, p = “aab",匹配,;
s = “absw”, p = ".”,匹配;
s = “aabb”, p = “a*b”,不匹配;思路:
- 递归法
首先考虑字符串为空情况:当p为空时,那么s若为空则满足匹配,若非空则表明不匹配。
然后通过递归思路考虑两种基本情况:
- 简单匹配:若不含*匹配模式,只需考虑两字符是否相同或p是否为‘.’,满足则跳过两字符串首元素继续匹配,否则返回false
- 包含* (p长度一定大于2):此时该字符一定位于p[1]处,需要考虑两种分支:
0次匹配:此时直接跳过p的前两字符,继续匹配;
1次匹配:化为了简单匹配的特殊情况,此时需要判断s长度,若s长度大于1,且与p首字符相同,则s跳过首字符,继续匹配。(由于递归后可以不断经过这两种分支,因此相当于实现了0次以上匹配的判断,只要其中存在满足的n次使得*的后续匹配成立,就返回true,即到达遍历终止条件)时间复杂度:o((S+P)2S+P/2)
空间复杂度:o((S+P)2S+P/2)
- 动态规划
采用二维数组记录匹配情况,dp[i,j]表示s的前i个与p的前j个的匹配情况。与递归相似:
首先考虑p是否为空,为空则根据s直接返回;
然后由于采用的是dp[i+1][j+1]=dp[i’][j’]的形式,所以需要将二维数组首行首列更新,首行显然只有dp[0][0]为真,首列需判断是否存在x*的形式,若存在,则判断是否为单个或多个x*的情况,即递归判断。
最后根据匹配情况更新二维表,
- 简单匹配时判断字符是否相等或p为‘.’,若满足则当前状态与两字符串回退一格的状态相同。
- 当匹配到*号时,有三种情况:
- 0次匹配:此时当前状态与p退两格状态相同
- 1次匹配:此时当前状态与p退一格状态相同
- 多次匹配:此时当前状态与s退一格状态相同
根据dp[len][len]判断是否满足匹配。
//java
//递归法
class Solution {
public boolean isMatch(String s, String p) {
if(p.isEmpty()) return s.isEmpty();//p是否为空
boolean fmatch = !s.isEmpty() && (p.charAt(0) == s.charAt(0) || p.charAt(0) == '.');
if(p.length() > 1 && p.charAt(1) == '*'){//*匹配
return (fmatch && isMatch(s.substring(1), p)) || isMatch(s, p.substring(2));//分支判断
}else{//简单匹配
return fmatch && isMatch(s.substring(1), p.substring(1));
}
}
}
//动态规划
class Solution {
public boolean isMatch(String s, String p) {
if(p.isEmpty()) return s.isEmpty();
boolean[][] dp = new boolean[s.length()+1][p.length()+1];
//初始化首行首列
dp[0][0] = true;
for(int t = 0; t < p.length(); ++t){
if(p.charAt(t) == '*' && dp[0][t-1]) dp[0][t+1] = true;
}
//动态规划
for(int i = 0; i < s.length(); ++i){
for(int j = 0; j < p.length(); ++j){
if(s.charAt(i) == p.charAt(j) || p.charAt(j) == '.'){
dp[i+1][j+1] = dp[i][j];//简单匹配
}else if(j > 0 && p.charAt(j) == '*'){
boolean fmatch = s.charAt(i) == p.charAt(j-1) || p.charAt(j-1) == '.';//遇到*时前一字符是否匹配
dp[i+1][j+1] = dp[i+1][j-1] || (fmatch && (dp[i][j+1] || dp[i+1][j]));//前面表示0次匹配,后面表示1次或多次匹配
}
}
}
return dp[s.length()][p.length()];
}
}