Skip to content

Instantly share code, notes, and snippets.

@dmjio
Created February 7, 2024 21:04
Show Gist options
  • Save dmjio/e11797b61db3809e8b83a7ae0b28d1dd to your computer and use it in GitHub Desktop.
Save dmjio/e11797b61db3809e8b83a7ae0b28d1dd to your computer and use it in GitHub Desktop.

Revisions

  1. dmjio created this gist Feb 7, 2024.
    116 changes: 116 additions & 0 deletions SuffixTree.hs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,116 @@
    import Data.List (foldl', tails)
    import Data.Map.Internal.Debug
    import qualified Data.Map.Strict as M
    import Data.Maybe
    import Data.Monoid
    import qualified Data.Set as S

    data TrieValue v
    = TrieValue
    { next :: Trie v
    , value :: v
    } deriving (Show, Eq)

    instance Semigroup v => Semigroup (TrieValue v) where
    TrieValue n1 tv1 <> TrieValue n2 tv2 =
    TrieValue (n1 <> n2) (tv1 <> tv2)

    -- | suffix trie, a trie populated with suffixes
    newtype Trie v = Trie (M.Map Char (TrieValue v))
    deriving (Eq, Show)

    type Todos = Trie (S.Set Int)

    instance Monoid k => Monoid (Trie k) where
    mempty = empty

    instance Semigroup k => Semigroup (Trie k) where
    Trie m <> Trie n = Trie (m <> n)

    empty :: Trie v
    empty = Trie mempty

    -- Assumes v is todo ID and String has no spaces, only a-z and '-'
    -- this inserts all suffixes into the trie
    fromList :: Monoid v => [(v, String)] -> Trie v
    fromList xs = foldr insertWithTails empty xs
    where
    insertWithTails (k,v) m =
    foldr (Main.insert k) m (tails v)

    fromList_ :: String -> Trie ()
    fromList_ vs = foldr (Main.insert ()) mempty (tails vs)

    insert
    :: Monoid v
    => v
    -> String
    -> Trie v
    -> Trie v
    insert _ [] t = t
    insert v (x:xs) (Trie m) =
    case M.lookup x m of
    Nothing ->
    Trie (M.insertWith (<>) x
    (TrieValue (insert v xs mempty) v) m)
    Just n ->
    Trie (M.insertWith (<>) x
    (TrieValue (insert v xs (next n)) v) m)

    search :: Monoid v => String -> Trie v -> v
    search [] _ = mempty
    search xs m = go xs m
    where
    go [x] (Trie n) =
    case M.lookup x n of
    Nothing -> mempty
    Just g -> value g

    go (x:xs) (Trie n) =
    case M.lookup x n of
    Nothing -> mempty
    Just g -> go xs (next g)

    size :: Trie v -> Int
    size (Trie m) =
    case M.size m of
    0 -> 0
    n -> n + sum (size . next <$> M.elems m)

    deleteById :: Int -> Todos -> Todos
    deleteById key (Trie m) = Trie $
    M.fromList
    [ (c, TrieValue (deleteById key n) (S.delete key keys))
    | (c, TrieValue n keys) <- M.toList m
    , S.singleton key /= keys
    ]

    main :: IO ()
    main = do
    let todos =
    [ ( S.singleton 1
    , "eat"
    ),
    ( S.singleton 2
    , "drink"
    ),
    ( S.singleton 3
    , "sleep"
    ),
    ( S.singleton 4
    , "yay"
    ),
    ( S.singleton 5
    , "foo"
    )
    ]
    let ts = fromList todos
    print $ search "e" ts
    print $ search "s" ts
    print $ search "ink" ts
    print $ search "eat" ts
    print $ search "a" ts
    print $ search "at" (deleteById 4 ts)