题目链接:44. 通配符匹配 - 力扣(LeetCode)
题目大意:
给你一个输入字符串 (s
) 和一个字符模式 (p
) ,请你实现一个支持 '?'
和 '*'
匹配规则的通配符匹配:
'?'
可以匹配任何单个字符。'*'
可以匹配任意字符序列(包括空字符序列)。
判定匹配成功的充要条件是:字符模式必须能够 完全匹配 输入字符串(而不是部分匹配)。
示例 1:
输入:s = "aa", p = "a" 输出:false 解释:"a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:s = "aa", p = "*" 输出:true 解释:'*' 可以匹配任意字符串。
示例 3:
输入:s = "cb", p = "?a" 输出:false 解释:'?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。
提示:
0 <= s.length, p.length <= 2000
s
仅由小写英文字母组成p
仅由小写英文字母、'?'
或'*'
组成
题目思路:
先通过递归理清关系,再记忆化搜索减少不必要的递归,最后动态规划实现
题目代码:
1.记忆化搜索
class Solution {
#define N 2010
typedef long long ll;
int n, m;
int dp[N][N];//-1,0,1
public:
bool f(string& s,string& p,int i,int j)
{
if (dp[i][j] != -1) {
return dp[i][j];
}
bool ans = false;
if (i == n) {
if (j == m) {
ans = true;
}
else {
if (p[j] == '*') {
ans= f(s, p, i, j + 1);
}
else {
ans = false;
}
}
}
else {//i!=n
if (j == m) {
ans = false;
}
else {//j!=m
if (s[i] == p[j] || p[j]=='?') {
ans = f(s, p, i + 1, j + 1);
}
else if (p[j] == '*') {
for (int k = i; k <= n;++k) {
ans = ans || f(s, p, k, j + 1);
}
}
else {
ans = false;
}
}
}
dp[i][j] = ans;
return ans;
}
bool isMatch(string s, string p) {
memset(dp, -1, sizeof(dp));
n = s.size();
m = p.size();
return f(s, p, 0, 0);
}
};
2.动态规划
class Solution {
#define N 2010
typedef long long ll;
int n, m;
int dp[N][N];//-1,0,1
public:
bool isMatch(string s, string p) {
memset(dp, 0, sizeof(dp));
n = s.size();
m = p.size();
dp[n][m] = 1;
for (int j = m - 1;j >= 0;--j) {
if (p[j] == '*') {
dp[n][j] = dp[n][j + 1];
}
}
for (int i = n - 1;i >= 0;--i) {
for (int j = m - 1;j >= 0;--j) {
if (s[i] == p[j] || p[j] == '?') {
dp[i][j] = dp[i + 1][j + 1];
}
else if (p[j] == '*') {
for (int k = i; k <= n;++k) {
dp[i][j] = dp[i][j] || dp[k][j + 1];
}
}
}
}
return dp[0][0];
}
};
3.优化枚举
class Solution {
#define N 2010
typedef long long ll;
int n, m;
int dp[N][N];//-1,0,1
public:
bool isMatch(string s, string p) {
memset(dp, 0, sizeof(dp));
n = s.size();
m = p.size();
dp[n][m] = 1;
for (int j = m - 1;j >= 0;--j) {
if (p[j] == '*') {
dp[n][j] = dp[n][j + 1];
}
}
for (int i = n - 1;i >= 0;--i) {
for (int j = m - 1;j >= 0;--j) {
if (s[i] == p[j] || p[j] == '?') {
dp[i][j] = dp[i + 1][j + 1];
}
else if (p[j] == '*') {
dp[i][j] = dp[i][j + 1] || dp[i + 1][j];
}
}
}
return dp[0][0];
}
};
4.使用vector减少无用的空间(数据量小的时候明显)
class Solution {
#define N 2010
typedef long long ll;
int n, m;
public:
bool isMatch(string s, string p) {
n = s.size();
m = p.size();
vector<vector<int>> dp(n+1,vector<int>(m+1));
dp[n][m] = 1;
for (int j = m - 1;j >= 0;--j) {
if (p[j] == '*') {
dp[n][j] = dp[n][j + 1];
}
}
for (int i = n - 1;i >= 0;--i) {
for (int j = m - 1;j >= 0;--j) {
if (s[i] == p[j] || p[j] == '?') {
dp[i][j] = dp[i + 1][j + 1];
}
else if (p[j] == '*') {
dp[i][j] = dp[i][j + 1] || dp[i + 1][j];
}
}
}
return dp[0][0];
}
};
5. 空间压缩(注意很多特殊情况的细节,空间足够的话不必要)
class Solution {
#define N 2010
typedef long long ll;
int n, m;
bool dp[N];
public:
bool isMatch(string s, string p) {
memset(dp, 0, sizeof(dp));
n = s.size();
m = p.size();
if(n==0 && m==0) return true;
if(m==0) return false;
dp[m] = 1;
for (int j = m - 1;j >= 0;--j) {
if (p[j] == '*') {
dp[j] = dp[j + 1];
}
}
for (int i = n - 1;i >= 0;--i) {
int rd = i == n - 1 ? 1 : 0;
for (int j = m - 1;j >= 0;--j) {
int cur = dp[j];
if (s[i] == p[j] || p[j] == '?') {
dp[j] = rd;
}
else if (p[j] == '*') {
dp[j] = cur || dp[j + 1];
}
else if (s[i] != p[j]) {
dp[j] = 0;
}
rd = cur;
}
}
return dp[0];
}
};