From 001ffc6789f9d77e34455a5a4576fc99b7b2fd3b Mon Sep 17 00:00:00 2001 From: Jens Kadenbach Date: Tue, 6 Dec 2022 18:14:27 +0100 Subject: [PATCH] Day6 with better algorithm --- src/Day6.hs | 39 +++++++++++++++++++++------------------ test/Day6Spec.hs | 14 +++++++------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/Day6.hs b/src/Day6.hs index e3da6bc..3ddbd25 100644 --- a/src/Day6.hs +++ b/src/Day6.hs @@ -1,32 +1,35 @@ module Day6 ( findEnd, findStart, - isUnique, day6 ) where -findMarker :: Int -> String -> Maybe Int -findMarker windowSize = findMarker' 0 "" - where - findMarker' :: Int -> String -> String -> Maybe Int - findMarker' offset window [] - | isUnique window && length window == windowSize = Just offset - | otherwise = Nothing - findMarker' offset window (c:cs) - | length window < windowSize = findMarker' (offset + 1) (c:window) cs - | not (isUnique window) = findMarker' (offset + 1) (c:init window) cs - | isUnique window = Just offset - | otherwise = Nothing +import qualified Data.Sequence as S +import Data.Sequence (Seq (..), (|>)) findStart :: String -> Maybe Int -findStart = findMarker 14 +findStart = findUniqueSubstring 14 findEnd :: String -> Maybe Int -findEnd = findMarker 4 +findEnd = findUniqueSubstring 4 -isUnique :: (Eq a) => [a] -> Bool -isUnique [] = True -isUnique (c:cs) = c `notElem` cs && isUnique cs +findUniqueSubstring :: Int -> String -> Maybe Int +findUniqueSubstring size input + | length input < size = Nothing -- imposible to find a substring because input is too small + | otherwise = find 0 S.empty $ S.fromList input + where + find :: Int -> Seq Char -> Seq Char -> Maybe Int + find _ _ Empty = Nothing + find off window (r :<| rs) + = case S.elemIndexL r window of -- is new element in window? + Nothing -> if S.length window == size -1 -- Element not found, but is our window large nough? + then Just nextOff -- Large enough, we got a winner! + else find nextOff enlargedWindow rs -- Not large enough, push new element to the end of the window + Just i -> find nextOff (removeConflict (i + 1)) rs -- continue after clearing window + where + nextOff = off + 1 + enlargedWindow = window |> r + removeConflict i = S.drop i enlargedWindow day6 :: IO () day6 = do diff --git a/test/Day6Spec.hs b/test/Day6Spec.hs index 7d4660c..dc573a9 100644 --- a/test/Day6Spec.hs +++ b/test/Day6Spec.hs @@ -2,21 +2,21 @@ module Day6Spec (spec) where import Test.Hspec import Day6 +import qualified Data.Sequence as S spec :: Spec spec = describe "Day6" $ do describe "Part1" $ do - it "finds unique lists" $ do - isUnique "abc" `shouldBe` True - isUnique "abca" `shouldBe` False - isUnique "cabc" `shouldBe` False - it "finds nothing if nothing is there" $ do - findEnd "abcb" `shouldBe` Nothing - findEnd "abcabcb" `shouldBe` Nothing + it "finds the marker just after the beginning" $ do + findEnd "aabcd" `shouldBe` Just 5 it "finds the marker in the beginning" $ do findEnd "abcd" `shouldBe` Just 4 findEnd "aabcd" `shouldBe` Just 5 + it "finds nothing if nothing is there" $ do + findEnd "abc" `shouldBe` Nothing + findEnd "abcb" `shouldBe` Nothing + findEnd "abcabcb" `shouldBe` Nothing it "finds a marker" $ do findEnd "mjqjpqmgbljsphdztnvjfqwrcgsmlb"`shouldBe` Just 7 findEnd "bvwbjplbgvbhsrlpgdmjqwftvncz" `shouldBe` Just 5