Значение, добавляемое в хеш с помощью символа (c
для краткости), которое вы удаляете, равно
ord(c) * radix^(length(pattern)-1)
, поскольку символ добавляет ord(c)
, когда он впервые входит в соответствующее окно, ихэш - следовательно, также его вклад - умножается на radix
для каждого из length(pattern)-1
символов, входящих в соответствующее окно, пока c
наконец не покинет его.
Но вы вычитаете ord(c) * radix * length(pattern)
$charArray = str_split($this->text);
$txtHash = (($txtHash + $this->prime)
- $this->radix * strlen($this->pattern)
* ord($charArray[$this->position]) % $this->prime)
% $this->prime;
$txtHash = ($txtHash * $this->radix + ord($character)) % $this->prime;
Кроме того, в расчете вы используете переменную $txtHash
, которую вы установили в 0, которая должна быть $this->previousHash
, и вы должны увеличить позицию текста.
В принципе,
$charArray = str_split($this->text);
$txtHash = (($this->previousHash + $this->prime)
- pow($this->radix, strlen($this->pattern)-1)
* ord($charArray[$this->position]) % $this->prime)
% $this->prime;
$txtHash = ($txtHash * $this->radix + ord($character)) % $this->prime;
$this->previousHash = $txtHash;
$this->position += 1;
- это то, что вам нужно сделать.
Но если шаблон не очень короткий, pow($this->radix,strlen($this->pattern)-1)
будет переполнен, поэтому вам придется заменить pow($this-radix, strlen($this->pattern)-1)
на модульнуюфункция возведения в степень
function mod_pow($base,$exponent,$modulus)
{
$aux = 1;
while($exponent > 0) {
if ($exponent % 2 == 1) {
$aux = ($aux * $base) % $modulus;
}
$base = ($base * $base) % $modulus;
$exponent = $exponent/2;
}
return $aux;
}
(она все еще может переполняться, если $modulus
, то есть $this->prime
здесь, слишком велико).Соответствующая строка кода становится
$txtHash = (($this->previousHash + $this->prime)
- mod_pow($this->radix, strlen($this->pattern)-1, $this->prime)
* ord($charArray[$this->position]) % $this->prime)
% $this->prime;
Тогда у вас потенциально огромная неэффективность
$this->text .= $character;
...
$charArray = str_split($this->text);
Если строка становится длинной, конкатенация и разбиение могут занять много времени (неуверен, как PHP реализует строки и строковые операции, но они, вероятно, не постоянное время).Вероятно, вам следует оставить только соответствующую часть строки, то есть удалить первый символ после пересчета хеша.