你总是可以做的一件事是将命令式代码直接翻译成 Haskell:
import qualified Data.Vector as V
twoPointers :: Int -> [Int] -> Maybe [Int]
twoPointers x xs = go 0 0 0 where
arr = V.fromList xs
n = V.length arr
go s l r
| r == n = Nothing
| s' == x = Just (V.toList (V.slice l (r + 1 - l) arr))
| s' <= x = go s' l (r + 1)
| otherwise = go (s - arr V.! l) (l + 1) r
where
s' = s + arr V.! r
编辑:以下函数式解决方案还不是 O(n),抱歉。
对于更实用的解决方案,我将推导出如下。您已经建议了scanl 和takeWhile,我会将它与tails 结合起来以获取每个子序列的所有总和的列表:
ghci> map (takeWhile (<= 5) . scanl (+) 0) (tails [1,2,3])
[[0,1,3],[0,2,5],[0,3],[0]]
(请注意,您可以改用一元绑定来编写它:takeWHile (<= 5) . scanl (+) 0 =<< tails [1,2,3],我认为它看起来更好)
有了这个,我们可以很容易地解决我们只想确定是否存在具有特定总和的子序列的决策问题版本:
subseqSumDecision :: Int -> [Int] -> Bool
subseqSumDecision x xs = x `elem` concatMap (takeWhile (<= x) . scanl (+) 0) (tails xs)
要获得实际的子序列,我认为最简单的方法是更改scanl 的参数以同时构建子序列:
ghci> concatMap (takeWhile ((<= 5) . fst) . scanl (\(s, xs) x -> (s + x, x : xs)) (0, [])) (tails [1,2,3])
[(0,[]),(1,[1]),(3,[2,1]),(0,[]),(2,[2]),(5,[3,2]),(0,[]),(3,[3]),(0,[])]
现在我们可以简单地使用lookup 函数来查找子序列:
subseqSumReversed :: Int -> [Int] -> Maybe [Int]
subseqSumReversed x xs = lookup x
$ concatMap
(takeWhile ((<= x) . fst) . scanl (\(s, xs) x -> (s + x, x : xs))
(0, []))
$ tails xs
你可能会注意到这会返回反转的子序列,所以你可以简单地在最后再次反转它:
subseqSum :: Int -> [Int] -> Maybe [Int]
subseqSum x xs = fmap reverse
$ lookup x
$ concatMap
(takeWhile ((<= x) . fst) . scanl (\(s, xs) x -> (s + x, x : xs))
(0, []))
$ tails xs