方法一:动态规划
m,n分别为s,p字符串长度,dp[i][j]代表s前i个字符与p前j个字符是否匹配;
1. dp[0][0] = true;两个字符串长度都为0,肯定是匹配的;
2. dp[0][1] 到 dp[0][k] 为true:代表字符串前面有连续的k个' * ',这些都是匹配的,因为 ’ * ‘可以代替空字符串,比如"" 和"***";
3. 若s[i-1]==p[j-1] 或者 p[j-1]==?则这两个字符是匹配的,即dp[i][j]与dp[i-1][j-1]的匹配情况一致;
4. 若p[j-1]=='*';因为 '*'可以代表空字符或者非空字符,所以它代表空字符串时,dp[i][j]=dp[i][j-1];代表非空字符时dp=dp[i-1][j];
notice:这并不是对应下标进行比较的,而是双循环,把所有情况都列出来
bool isMatch(string s, string p)
{
int i = 0, j = 0, m = s.length(), n = p.length(); //dp[i][j]代表s的前i个字符,p的前j个字符能够匹配;
bool dp[m + 1][n + 1];
for (int i = 0; i <= m; i++) //初始化全部为false
{
for (int j = 0; j <= n; j++)
{
dp[i][j] = false;
}
}
dp[0][0] = true;
for (int k = 1; k <= n; k++) //将前面k个连续的'*'对应的dp[0][k]置为true
{
if (p[k - 1] == '*')
dp[0][k] = true;
else
{
break;
}
}
//双循环,注意这里的 i,j 表示对应字符串 s 前i个字符 与 p 前j个字符是否匹配 他们转化为下标时时i-1 和 j-1;
for (i = 1; i <= m; i++)
{
for (j = 1; j <= n; j++)
{
if (s[i - 1] == p[j - 1] || p[j - 1] == '?')
{
dp[i][j] = dp[i - 1][j - 1];
}
else if (p[j - 1] == '*')
{
dp[i][j] = dp[i][j - 1] || dp[i - 1][j];
}
}
}
return dp[m][n];
}
方法二:贪心 + 双指针 (回溯法)
利用双指针 istar 和 jstar 来存储匹配不成功后需要回溯的位置,多次匹配只有在字符串p中出现’ * '之后;
因为它并不是玩玩全全的双循环,只是在一个循环中可能要在进行一个部分循环,所以复杂度必定低于动态规划;
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;//记录当p[j]==’*‘时,i此时的位置
jStar = j++;//记录j的位置 并且j移到下一位 准备次s[i]和p[j]的匹配
}
else if (iStar >= 0)
{//发现字符不匹配且没有星号出现 但是istar>0 说明可能是*匹配的字符数量不对 这时回溯
i = ++iStar;//i回溯到istar+1 因为上次从s串istar开始对*的尝试匹配已经被证明后面存在不匹配情况(不然不会落入此分支) 所以需要从istar+1再次开始试 同时 istar 更新回溯位置,就是找到下一个s[istar]==p[jstar+1]中istar的位置
j = jStar + 1;//j回溯到jstar+1 并保持位置 直到s[istar]==p[j]时重新开始匹配
} else return false;
}
while (j < n && p[j] == '*') ++j;//去除剩下多余的星号
return j == n;//如果星号后还有非星号字符,则j != n ,则返回false;否则返回true;
}