Knuth-Morris-Pratt在Haskell中的实现 - 索引超出范围

时间:2016-01-14 22:33:30

标签: algorithm haskell knuth-morris-pratt

我曾使用维基百科的伪代码试图在Haskell中编写KMP算法。

它给出了#34;索引越界"当我试图超越模式的长度进行搜索时,我似乎无法找到问题;我的修复"只破坏了结果。

import Control.Monad
import Control.Lens
import qualified Data.ByteString.Char8 as C
import qualified Data.Vector.Unboxed as V

(!) :: C.ByteString -> Int -> Char
(!) = C.index

-- Make the table for the KMP. Directly from Wikipedia. Works as expected for inputs from Wikipedia article.
mkTable :: C.ByteString -> V.Vector Int
mkTable pat = make 2 0 (ix 0 .~ (negate 1) $ V.replicate l 0)
    where
        l = C.length pat

        make :: Int -> Int -> V.Vector Int -> V.Vector Int
        make p c t
            | p >= l    = t
            | otherwise = proc
            where
                proc | pat ! (p-1) == pat ! c
                                 = make (p+1) (c+1) (ix p .~ (c+1) $ t)
                     | c > 0     = make p (t V.! c) t
                     | otherwise = make (p+1) c (ix p .~ 0 $ t)

kmp :: C.ByteString -> C.ByteString -> V.Vector Int -> Int
kmp text pat tbl = search 0 0
    where
        l = C.length text
        search m i
            | m + i >= l = l
            | otherwise  = cond
            where
                -- The conditions for the loop, given in the wiki article
                cond | pat ! i == text ! (m+i)
                          = if i == C.length pat - 1
                            then m
                            else search m (i+1)
                     | tbl V.! i > (-1)
                          = search (m + i - (tbl V.! i)) (tbl V.! i)
                     | otherwise
                          = search 0 (m+1)

main :: IO()
main = do
    t <- readLn
    replicateM_ t $ do
        text <- C.getLine
        pat  <- C.getLine
        putStrLn $ kmp text pat (mkTable pat)

1 个答案:

答案 0 :(得分:1)

简单的解决方案:我在kmp的最后一个条件下混合了m和i。

| otherwise = search 0 (m+1)

变为

| otherwise = search (m+1) 0

问题已经解决。

除此之外,有必要在ST monad中使用未装箱的阵列,否则表格生成需要花费大量时间。