我正在尝试为Hackerrank的一个问题编写解决方案。挑战是计算列表中的元素,元素从0到99不等,因此可以在线性时间内计算它们。这是我得到的:
{-# LANGUAGE BangPatterns #-}
{-# OPTIONS_GHC -O3 #-}
module Main where
import Data.STRef
import Data.Foldable
import Control.Monad
import Control.Monad.ST
main = do
line1 <- getLine
line2 <- getLine
let
!ns = map read $ words line2 :: [Int]
res = runST $ do
refs <- forM [0..99] $ \i ->
newSTRef (0 :: Int)
traverse_ (\x -> modifySTRef' (refs !! x) (+1) ) ns
mapM (\ref -> readSTRef ref) refs
putStrLn . unwords . map show $ res
此代码有效,但速度不够快,无法通过最后一个测试用例。有人可以推荐一项改进吗? (link to the problem)
答案 0 :(得分:6)
这可以使用Data.Array
中的accumArray
作为一行进行。类似于accumArray (+) 0 (0,99) . zip values $ repeat 1
,其中values
是输入。
它似乎仍然不够快,这有点令人烦恼。 accumArray
或多或少尽可能高效。在我的系统上进行测试显示处理1,000,000个输入值的时间约为1秒,即使没有编译也是如此,并且该时间主要是生成随机输入。这与测试网站上的5秒相差甚远。我不得不怀疑该系统是否过载。
答案 1 :(得分:3)
您遇到的一个问题是,您在列表中查找STRef
,这意味着每次查找和修改都需要遍历O(n)
步骤。这可以通过使用Data.Map.Map
具有O(log(n))
查找和修改时间的内容来缓解。
您还可以在Array
monad中使用可变Vector
或O(1)
ST
查找/修改时间。这可能是最快的方法。