题目:
给定一个字符串 (s) 和一个字符模式 § ,实现一个支持 ‘?’ 和 ‘*’ 的通配符匹配。
'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。
说明:
- s可能为空,且只包含从 a-z 的小写字母。
- p可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *
示例:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
输入:
s = "aa"
p = "*"
输出: true
解释: '*' 可以匹配任意字符串。
输入:
s = "cb"
p = "?a"
输出: false
解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。
输入:
s = "adceb"
p = "*a*b"
输出: true
解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce".
输入:
s = "acdcb"
p = "a*c?b"
输出: false
基本思想:
动态规划,
创建二维数组dp,dp[i][j]表示p串的前i个字符和s串的前j个字符是否匹配。
状态转移方程:
注意p,s下标从0开始
dp[i][j]= dp[i-1][j-1] p[i-1]=s[i-1] || p[i-1]="?"
dp[i][j]=dp[i-1][j] || dp[i][j-1] p[i-1]="*"
解释:
当p[i-1]=s[i-1]时,显然第i个字符和第j个字符相等,或者当p[i-1]="?"时,表示当前第i个字符为“?”,表示可以代替任何单个字符,这时前i个和前j个是否匹配就取决于前i-1和前j-1个是否匹配。
当p[i-1]=" * ",题目已经说明, * 可以吃掉任意长的连续字符串, * 就有两种选择:1. * 不吃字符:2. * 吃掉当前字符。
dp[i-1][j]表示 * 不吃掉第j个字符,所以拿前i-1个去匹配前j个。
dp[i][j-1] 表示吃掉当前字符。我用前i个(即最后为 * )去匹配前j-1个,无视第j个,为什么能无视?因为 * 可以吃掉第j个,所以 * 的存在完全可以不用关心第j个字符。为什么是拿前i个字符?因为最后一位的 * 也可能继续匹配第j-1个字符。
初始状态:
dp[0][0]表示空串匹配空串,为true
由于串p开头可能有连续 * ,而 * 可以表示空,前i个都是 * 也可表示空 。假设前i个都是 * ,那么就有dp[0][0]…dp[i][0]都为true。
代码
class Solution {
public boolean isMatch(String s, String p) {
int m=p.length();
int n=s.length();
boolean[][] dp=new boolean[m+1][n+1];
dp[0][0]=true;//设定初始状态
char[] sArray=s.toCharArray();
char[] pArray=p.toCharArray();
for(int i=1;i<=m;i++){
//p串前连续为*的情况,设定初始态
if(pArray[i-1]!='*')
break;
dp[i][0]=true;
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(pArray[i-1]==sArray[j-1]||pArray[i-1]=='?')
//状态转移方程1
dp[i][j]=dp[i-1][j-1];
else if(pArray[i-1]=='*')
//状态转移方程2
//第一个为*不匹配当前字符
//第二个为*匹配当前字符
dp[i][j]=dp[i-1][j]||dp[i][j-1];
}
}
return dp[m][n];
}
}