1、题目描述:
2、题解:
方法1:动态规划
动态规划问题,弄清楚三点:
1、重复子问题;
2、最优子结构;
3、无后效性。
动态规划:
1、状态定义;
2、状态转移方程;
3、初始化;base case
4、输出;
5、思考状态压缩。
可以用递归去求,但是会存在重叠子问题,加个备忘录可以解决重复问题。
DP table 从左上到右下。
状态定义:
dp[i][j] s的前i个字符和p的前j个字符是否匹配,匹配为True,不匹配为False
状态转移方程:
如果s[i] == p[j] or p[j] == '?':
dp[i][j] = dp[i-1][j-1]
解释:也就是i,j位置的字符匹配了,那么此时的状态就等于s的前i-1个字符与p的前j-1个字符的匹配情况
如果p[j-1] == '*' :
dp[i][j] = dp[i-1][j] or dp[i][j-1]
解释:dp[i-1][j],'*'匹配多个
dp[i][j-1],'*'未匹配或者理解成匹配了空字符串
画图分析:
base case:
1)dp[0][0] = True,解释:s,p都是空字符,一定匹配
2)s为空字符,p要想匹配,只有'*'才能实现('?'一定需要匹配字符,'*'可匹配空字符串)
if p[j] == '*': dp[0][j] = dp[i][j-1]
3)s非空,p空字符,那么一定不匹配
python代码如下:
class Solution:
def isMatch(self, s: str, p: str) -> bool:
dp = [[False] * (len(p) + 1) for _ in range(len(s) + 1)]
dp[0][0] = True
for j in range(1,len(p) + 1):
if p[j - 1] == '*':
dp[0][j] = dp[0][j - 1]
for i in range(1,len(s) + 1):
for j in range(1,len(p) + 1):
if s[i - 1] == p[j - 1] or p[j - 1] == '?':
dp[i][j] = dp[i - 1][j - 1]
elif p[j - 1] == '*':
dp[i][j] = dp[i - 1][j] or dp[i][j - 1] #分别为匹配多个,和匹配一个
return dp[-1][-1]
C++代码如下:
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size();
int n = p.size();
vector<vector<int>> dp(m + 1,vector<int>(n+1));
dp[0][0] = true;
for (int j = 1;j <= n;++j){
if (p[j - 1] == '*'){
dp[0][j] = dp[0][j-1];
}
}
for(int i = 1;i <= m;++i){
for (int j = 1;j <= n;++j){
if (p[j - 1] == '?' || s[i - 1] == p[j - 1]){
dp[i][j] = dp[i - 1][j - 1];
}else if (p[j - 1] == '*'){
dp[i][j] = dp[i-1][j] | dp[i][j - 1];
}
}
}
return dp[-1][-1];
}
};
方法2:贪心算法:
class Solution {
public:
bool isMatch(string s, string p) {
int i = 0, j = 0, iStar = -1, jStar = -1, m = s.size(), n = p.size();
while (i < m) {
if (j < n && (s[i] == p[j] || p[j] == '?')) {
++i, ++j;//i,j向后瞬移
} else if (j < n && p[j] == '*') {//记录如果之后序列匹配不成功时, i和j需要回溯到的位置
iStar = i;//记录星号
jStar = j++;//记录星号 并且j移到下一位 准备下个循环s[i]和p[j]的匹配
} else if (iStar >= 0) {//发现字符不匹配且没有星号出现 但是istar>0 说明可能是*匹配的字符数量不对 这时回溯
i = ++iStar;//i回溯到istar+1 因为上次从s串istar开始对*的尝试匹配已经被证明是不成功的(不然不会落入此分支) 所以需要从istar+1再开始试 同时inc istar 更新回溯位置
j = jStar + 1;//j回溯到jstar+1 重新使用p串*后的部分开始对s串istar(这个istar在上一行已经inc过了)位置及之后字符的匹配
} else return false;
}
while (j < n && p[j] == '*') ++j;//去除多余星号
return j == n;
}
};
3、复杂度分析
方法1:
时间复杂度:O(MN)
空间复杂度:O(MN)