写在前面:本文侧重诠释对算法的思考记录过程,忽略其他诸如代码简洁、字符编码等细节问题。
<?php
$str = 'goauabcdislcdnabcdabcdvbuijdks';
$target = 'abcdabcd';
$start = bf_str($str, $target);
$start2 = kmp_str($str, $target);
var_dump($start);
var_dump($start2);
//BF算法:暴力匹配算法或朴素匹配算法,即从被查找的主串的起始位置开始,
//依次向前比对每个位置上的字符,以找出与要查找的目标模式串值和数量都匹配的字符串
//算法复杂度:O(n*m) 注:n为主串的长度,m为模式串长度
function bf_str($str, $target) {
$n = strlen($str);
$m = strlen($target);
$j = 0;
$i = 0;
$start = -1;
for (; $i<$n; $i++) {
if ($j < $m && $str[$i] != $target[$j]) {
$j = 0;
continue;
} else {
$j++;
if ($j >= $m) {
$start = $i - $m + 1;
break;
}
}
}
return $start;
}
//KMP算法:在模式串与主串匹配的过程中,当遇到不可匹配的坏字符的时候,就跳过最长可匹配前缀,
//重新开始匹配,从而避免 BF 算法这种暴力匹配,提高算法性能。
//算法复杂度:O(n+m)
function kmp_str($str, $target) {
$n = strlen($str);
$m = strlen($target);
$next = generate_next($target);
$j = 0;
$start = -1;
for ($i=0; $i<$n; $i++) {
// $stri = $str[intval($i)];
// $targetj = $target[intval($j)];
$stri = substr($str, $i, 1);
$targetj = substr($target, $j, 1);
while ($j > 0 && $j < $m && $stri != $targetj) {
$j = $next[$j-1] + 1;
}
if ($stri == $targetj) {
$j++;
}
if ($j == $m) {
$start = $i - $m + 1;
break;
}
}
return $start;
}
function generate_next($target) {
$next = [];//所有好前缀
$next[0] = -1;
for ($i=1; $i<strlen($target)-1; $i++) {
$prefix = substr($target, 0, $i+1);
$next[$i] = get_repeat_last_num($prefix);
}
return $next;
}
function get_repeat_last_num($str) {
$middle = floor((strlen($str) - 1) / 2);
$num = -1;//最长可匹配前缀结尾字符下标
for ($i=$middle; $i>=0; $i--) {
$left = substr($str, 0, $i+1);
$right = substr($str, -$i-1);
if ($left == $right) {
$num = $i;
break;
}
}
return $num;
}
参考来源:https://xueyuanjun.com/books/data-structure-and-algorithms