改进的模式匹配算法KPM算法
算法思想:在主串S中对串T进行模式匹配时,当第i位失配时,并不是将T向后滑动一位,而是滑动next[i]位,算法关键是需要知道串T中每一位对应的next值。next值为T的每一位的最长前后缀匹配长度,当第i位失配后,就将T向后滑动 i - next[i]位。这样可以保证移动后的T中next[i]前的子串与S串中的相应位置的子串值都相等。
next值得计算算法
算法思想:next中的值是T串中的每一位的最大前后缀匹配长度 + 1。
/**
* @name 字符串匹配KPM
* @use 使用KPM算法进行字符串匹配
* @param sourceStr 主字符串
* @param target 需要匹配的字符串
* @return int|false 匹配到位置|未匹配到
*/
public static function strPosKPM($sourceStr, $target)
{
$next = self::countMaxMateLen($target);
$j = 0;
$index = false;
for ($i = 0; $i < strlen($sourceStr); $i++) {
if (substr($sourceStr, $i, 1) !== substr($target, $j, 1)) {
$j = $next[$j];
if ($j != 0) {
--$i;
}
continue;
}
++$j;
if ($j >= strlen($target)) {
$index = $i - strlen($target) + 1;
break;
}
}
return $index;
}
/**
* @name 计算最大前后缀匹配长度
* @use 计算一个字符串的最大前后缀匹配长度,用于进行KPM算法时使用
* @param str 字符串
* @return resutl 含有字符串每一位最大前后缀匹配长度的数组
*/
public static function countMaxMateLen($str) {
$length = strlen($str);
$result = [0 => -1];
$i = 0;
$j = -1;
while ($i < $length - 1) {
if ($j == -1 || substr($str,$i,1) == substr($str,$j,1)) {
++$j;
++$i;
$result[$i] = $j;
} else {
$j = $result[$j];
}
}
$result[0] = 0;
return $result;
}