方法1:动态规划
/**
* 方法1: 动态规划,令dp[i][j]表示,s[0~i-1] 与 p[0~j-1]是否匹配,对s、p而言,下标从0开始,而dp而言,下标表示第几个字符
* 状态转移方程为:
* 1. 当p[j-1]不为"*"时 :当s[i-1] 与 p[j-1]相等 或 p[j-1]是 "?"时, dp[i][j] = dp[i-1][j-1];
* 2. 当p[j-1]为"*"时 : dp[i][j] = dp[i-1][j] || dp[i][j-1] 解析:若dp[i-1][j]为真,则无论s[i-1]是什么,dp[i][j]都是真
* 若dp[i][j-1]为真,则当p[j-1]为"*"时,dp[i][j]也为真。
*
* 边界条件:
* 显然,dp[0][0] = true; dp[i][0]为false; 至于dp[0][j],当p的前j个字符为"*"时,dp[0][j]=true,否则为false;
* @param String $s
* @param String $p
* @return Boolean
*/
function isMatch($s, $p) {
$sLen = strlen($s);
$pLen = strlen($p);
// 先将dp中的每一个元素初始化为false
$dp = array_fill(0, $sLen+1, array_fill(0, $pLen+1, false));
$dp[0][0] = true;
for ($j=1; $j<=$pLen; $j++) {
// 如果前j个字符均为"*",则dp[0][j]为true
if ($p[$j-1] == "*") {
$dp[0][$j] = true;
} else {
break;
}
}
for ($i=1; $i<=$sLen; $i++) {
for ($j=1; $j<=$pLen; $j++) {
if ($p[$j-1] == '*') {
$dp[$i][$j] = $dp[$i-1][$j] || $dp[$i][$j-1];
continue;
}
if ($p[$j-1] == '?' || $s[$i-1] == $p[$j-1]) {
$dp[$i][$j] = $dp[$i-1][$j-1];
}
}
}
return $dp[$sLen][$pLen];
}
方法二: 贪心算法
/**
* 使用贪心算法(对于*abc*xyz*这种类型,s字串中应该尽可能早地匹配到 abc ,再然后是xyz)
* 当然p的结尾可能不是"*",则就应该和s的结尾开始完全匹配到有"*"的地方,或p字串开头
* 若p的开头不是"*",则处理方法同结尾不是"*", 这儿可以用一个巧妙的方法,设sRecord 和 pRecord 的初始值为 -1,
* 表示模式 p 的开头字符不是星号,并且在匹配失败时进行判断,如果它们的值仍然为 -1,说明没有「反悔」重新进行匹配的机会
* @param $s
* @param $p
*/
function isMatchV2($s, $p) {
$sEnd = strlen($s) - 1;
$pEnd = strlen($p) - 1;
$isMatch = function($a, $b) {
return $a == $b || $b == '?';
};
$allStars = function ($p, $start, $end) {
while ($start <= $end) {
if ($p[$start] != '*') {
return false;
}
++$start;
}
return true;
};
// 从后向前,将p中非"*"字符匹配掉
while ($sEnd >= 0 && $pEnd >= 0) {
if ($isMatch($s[$sEnd], $p[$pEnd])) {
--$sEnd;
--$pEnd;
} else if ($p[$pEnd] == '*') {
break;
} else {
return false;
}
}
// 若是p匹配到了开头
if ($pEnd < 0) {
return $sEnd < 0;
}
$sIdx = 0;
$pIdx = 0;
$sRecord = -1;
$pRecord = -1;
while ($sIdx <= $sEnd && $pIdx <= $pEnd) {
if ($p[$pIdx] == '*') {
// 之前的匹配段已经通过,更新record的值
++$pIdx;
$sRecord = $sIdx;
$pRecord = $pIdx;
} else if ($isMatch($s[$sIdx], $p[$pIdx])) {
// 如果当前遍历到的字符匹配,则继续检测下一个字符
++$sIdx;
++$pIdx;
} else if ($sRecord != -1 && $sIdx < $sEnd) {
// 如果不匹配,则回溯到s开始匹配的下一个字符,继续检测
++$sRecord;
$sIdx = $sRecord;
$pIdx = $pRecord;
} else {
return false;
}
}
return $allStars($p, $pIdx, $pEnd);
}