1

我将如何为流实施Boyer-Moore 搜索?我了解如何为给定的字符串实现这一点,我们知道整个字符串的长度。但是,如果我们不知道字符串的大小(即,它是任意长度的字节流)怎么办。

我正在尝试在 PHP 中实现这一点,因此 PHP 中的任何代码都会有所帮助。

这是我在 PHP 中的 Boyer-Moore Search 的实现:

function BoyerMooreSearch($haystack, $needle) {
    $needleLen = strlen($needle);
    $haystackLen = strlen($haystack);
    $table = computeSkipTable($needle);

    for ($i = $needleLen - 1; $i < $haystackLen;) { 
        $t = $i;

        for ($j = $needleLen - 1; $needle[$j] == $haystack[$i]; $j--, $i--) { 
            if($j == 0) {
                return $i;
            }
        }

        $i = $t;

        if(array_key_exists($haystack[$i], $table)) {
            $i = $i + max($table[$haystack[$i]], 1);
        } else {
            $i += $needleLen;
        }
    }
    return false;
}

function computeSkipTable($string) {
    $len = strlen($string);
    $table = [];

    for ($i=0; $i < $len; $i++) { 
        $table[$string[$i]] = $len - $i - 1; 
    }

    return $table;
}

如果我们给它一个 haystack string like"barfoobazquix"和一个 needle string like "foo",这很好用,它将3按预期返回。但是,如果输入 haystack 是一个拆分为 4 字节块的流怎么办。第一个块是"barf",它将不返回匹配项,第二个块"ooba"也是不返回匹配项,依此类推......

在这种情况下,我们永远无法"foo"在当前实现的流的任何缓冲块中找到子字符串。我正在努力调整当前的实现,即使搜索字符串被分成多个块,它也能正常工作。

4

1 回答 1

1

请注意,您只使用$needleLen流中最新的字符。所以你可以维护一个由$needleLen字符组成的滑动窗口,并根据需要推进它。此外,$haystackLen现在未知,应替换为 EOF 检查。

O(n)下面的代码效率低下,因为无论我们是按字符滑动还是仅滑动 1 ,滑动窗口总是需要花费n。为了实现最佳的滑动复杂度,$window应该将其实现为循环字符缓冲区。

// sample call
var_dump(BoyerMooreSearch(STDIN, 'foo'));

function BoyerMooreSearch($resource, $needle) {
    $needleLen = strlen($needle);
    $table = computeSkipTable($needle);

    // build the first window
    if (($window = buildWindow($resource, $needleLen)) === false) {
        // special case: the text is shorter than the pattern
        return false;
    }

    $i = 0;
    while (true) {
        // check for a match
        $j = $needleLen - 1;
        while ($j >= 0 && $needle[$j] == $window[$j]) {
            $j--;
        }
        if ($j < 0) {
            return $i;
        }

        // determine slide distance
        $t = $i;
        $last = substr($window, -1);
        if (array_key_exists($last, $table)) {
            $i = $i + max($table[$last], 1);
        } else {
            $i += $needleLen;
        }

        // slide the window
        if (($window = slideWindow($window, $resource, $i - $t)) === false) {
            return false;
        }
    }

    return false;
}

/**
 * Initializes the sliding window to length $len.
 *
 * @return string A string of length $len or false if the $resource contains
 * less than $len characters.
 */
function buildWindow ($resource, $len) {
    $result = '';
    while ($len--) {
        $result .= fgetc($resource);
    }
    return feof($resource) ? false : $result;
}

/**
 * Slides $window by $count positions into $resource.
 *
 * @return string The new window or false on EOF.
 */
function slideWindow(&$window, $resource, $count) {
    $window = substr($window, $count) // discard the first $count chars
        . fread($resource, $count);   // and read $count new chars
    return feof($resource) ? false : $window;
}
于 2020-04-06T09:42:22.593 回答