module Algorithms.StringSearch.KMP( isSubStringOf
, kmpMatch
, buildFailureFunction
) where
import Control.Monad.ST
import qualified Data.Vector as V
import Data.Vector.Generic ((!))
import qualified Data.Vector.Unboxed as UV
import qualified Data.Vector.Unboxed.Mutable as UMV
import qualified VectorBuilder.Builder as Builder
import qualified VectorBuilder.Vector as Builder
buildFailureFunction :: forall a. Eq a => V.Vector a -> UV.Vector Int
buildFailureFunction p = UV.create $ do
f <- UMV.new m
go f 1 0
where
m = V.length p
go :: UMV.MVector s Int -> Int -> Int -> ST s (UMV.MVector s Int)
go f i j | i == m = pure f
| p ! j == p ! i = UMV.write f i (j+1) >> go f (i+1) (j+1)
| j > 0 = UMV.read f (j-1) >>= go f i
| otherwise = UMV.write f i 0 >> go f (i+1) 0
isSubStringOf :: (Eq a, Foldable p, Foldable t) => p a -> t a -> Maybe Int
p `isSubStringOf` t = kmpMatch (Builder.build . Builder.foldable $ p)
(Builder.build . Builder.foldable $ t)
kmpMatch :: Eq a => V.Vector a -> V.Vector a -> Maybe Int
kmpMatch p t | m == 0 = Just 0
| otherwise = kmp 0 0
where
m = V.length p
n = V.length t
f = buildFailureFunction p
kmp i j | i == n = Nothing
| p ! j == t ! i = if j == m - 1 then Just (i - m + 1)
else kmp (i+1) (j+1)
| j > 0 = kmp i (f ! (j - 1))
| otherwise = kmp (i+1) 0