Вот немного длинное решение, но гарантированно будет наихудший O (n log n):
import List
import Data.Ord.comparing
sortPairs :: Ord a => [(a, b)]->[(a, b)]
sortPairs = sortBy (comparing fst)
index :: Integral b => [a] -> [(a, b)]
index = flip zip [1..]
dropRepeated :: Eq a => [(a, b)]->[(a, b)]
dropRepeated [] = []
dropRepeated [x] = [x]
dropRepeated (x:xs) | fst x == fst (head xs) =
dropRepeated $ dropWhile ((==(fst x)).fst) xs
| otherwise =
x:(dropRepeated xs)
nonRepeatedPairs :: Ord a => Integral b => [a]->[(a, b)]
nonRepeatedPairs = dropRepeated . sortPairs . index
firstNonRepeating :: Ord a => [a]->a
firstNonRepeating = fst . minimumBy (comparing snd) . nonRepeatedPairs
Идея состоит в том, чтобы: отсортировать строку лексикографически, чтобы можно было легко удалить любые повторяющиеся символы за линейное время и найти первый символ, который не повторяется. Но чтобы найти его, нам нужно сохранить информацию о позициях персонажей в тексте.
Скорость в простых случаях (например, [1..10000]
) не идеальна, но для чего-то более сложного ([1..10000] ++ [1..10000] ++ [10001]
) вы можете увидеть разницу между этим и наивным O (n ^ 2).
Конечно, это можно сделать за линейное время, если размер алфавита равен O (1), но кто знает, насколько велик алфавит ...