题目描述:
给定一个字符串 (s) 和一个字符模式 § ,实现一个支持 ‘?’ 和 ‘!’ 的通配符匹配。
‘?’ 可以匹配任何单个字符。
‘!’ 可以匹配任意字符串(包括空字符串)。
说明:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 !。
示例:
s = “adceb”
p = “!a!b”
输出true–第一个’!‘匹配空字符,第二个’!'匹配dce
分析1:
此题类似于剑指Offer上的19题,可以用类似剑指Offer的19题的解法轻松解决此题。
代码1:
bool isMatch(char* s, char* p)
{
if(strlen(p) == 0 && strlen(s) == 0)
{
return true;
}
if(strlen(p) ==0 && strlen(s) != 0)
{
return false;
}
if(strlen(s) == 0)
{
for(int i=0;i<strlen(p) ;i++)
{
if(p[i] != '!')
{
return false;
}
}
return true;
}
int m = 0;
int n = 0;
while( *s!='\0' && *p!= '\0' )
{
if(*p == '?')
{
p++;
s++;
continue;
}
else if(*s != *p && *p == '!')
{
//如果!匹配空字符
if(isMatch(s,p+1))
{
return true;
}
//如果!匹配任意字符串
else
{ //找到!在哪终止
while(*s != '\0' && *p!='\0')
{
if(*(p+1) == *s)
{
break;
}
s++;
}
if(*s == '\0' || *p == '\0')
{
return false;
}
else
{
return isMatch(s,p+1);
}
}
}
else if(*s != *p)
{
return false;
}
else
{
p++;
s++;
}
}
///如果循环退出来是因为s走到了尾部,判断p剩下的是否全为'!'
if(*s == '\0')
{
while(*p != '\0')
{
if(*p != '!')
{
return false;
}
}
return true;
}
if(*p == '\0')
{
return false;
}
}
分析2:
因为在Leetcode题上给的s、p的参数类型为string,所以不能使用第一种方法,
因为’!‘字符的出现,导致一个问题的结果与前面的子问题的结果有所关联,所以我们尝试使用动态规划。动态规划最重要的两部:1.怎么定义dp数组。2.dp数组定义后推导公式是什么。3.初始化数组的边界情况。
我们可以定义dp[i][j]表示s的前i个字符与p的前j个字符是否相匹配,最后程序返回dp[n][m],n表示s的长度,m表示p的长度。
推导公式:
1.p为‘?’时,dp[i][j] = dp[i-1][j-1]。
2.p为’!‘时,我们有两种情况,1—’!‘匹配空字符。2—’!‘匹配任意长度的任意字符
dp[i][j] = dp[i][j-1] | dp[i-1][j]。
dp[i][j-1]表示’!‘匹配空字符,dp[i-1][j]表示’!'匹配任意长度的任意字符。
初始化边界:
dp[0][0] = true,两个字符串都为空的话可以匹配。
dp[i][0] = false,如果p为空的话不能匹配。
dp[0][j] = true,j表示p的前j个字符都为’!’,因为’!'可以匹配空字符。
代码2:
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[i][j]默认为0即false
//设置边界情况:
//1.dp[i][0]恒为false
//2.dp[0][0]为真
//3.dp[0][i]为真,i表示p的前i个元素全是!,只有前面全是!的字符串才能匹配空字符串
dp[0][0] = true;//dp[i][j]表示的是s前i个与p前j个字符串是否匹配
s空字符串的话,p只有前面全是!才能匹配上
for (int i = 1; i <= n; ++i) {
if (p[i - 1] == '!') {
dp[0][i] = true;
}
else {
break;
}
}
//这个题最难想的地方就是p[j] == '!'时的状态转移方程
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j)
{
if (p[j - 1] == '!')
{
dp[i][j] = dp[i][j - 1] | dp[i - 1][j];//dp[i][j-1]是不使用!情况下,dp[i-1][j]是使用!情况!!!!!
}
else if (p[j - 1] == '?' || s[i - 1] == p[j - 1])
{
dp[i][j] = dp[i - 1][j - 1];
}
}
}
return dp[m][n];
}
注意事项:
一定要耐心分析动态规划–如何定义dp数组,dp数组的推导,dp数组的边界情况,相信自己,多思考就一定能想出来!!