Posts Tagged ‘question’

Programming Praxis – First Unrepeated Character In A String

April 30, 2013

In today’s Programming Praxis exercise, our goal is to find the find the first unrepeated character in a string, along with its index. Let’s get started, shall we?

import Data.Maybe
import Data.List

We traverse the list from right to left, keeping a list of characters we’ve already encountered and a list of possible options. When we check a character, we first check if it’s in the list of duplicate characters. If not, we then check the list of options. If the letter in question is there already, we remove it from the list of options and add it to the list of duplicates. Otherwise, we add the current character to the list of options. At the end, we return the first unique element (if any).

unrepeated :: String -> Maybe (Integer, Char)
unrepeated = listToMaybe . snd . foldr f ([], []) . zip [0..] where
    f (i,c) (ds,us) = if elem c ds then (ds, us) else
        maybe (ds, (i,c):us) (\(fi,fc) -> (fc:ds, delete (fi,fc) us)) $
        find ((== c) . snd) us

Some tests to see if everything is working properly:

main :: IO ()
main = do print $ unrepeated "aaabc"       == Just (3, 'b')
          print $ unrepeated "aaabbbcddd"  == Just (6, 'c')
          print $ unrepeated "aaaebbbcddd" == Just (3, 'e')
          print $ unrepeated "aabbcc"      == Nothing
          print $ unrepeated "aba"         == Just (1, 'b')

Programming Praxis – Amazon Interview Question

November 27, 2012

In today’s Programming Praxis exercise, our goal is to find the 100 points closest to (0, 0) out of a list of 1000000 random points in O(n). Let’s get started, shall we?

Some imports:

import Control.Applicative
import qualified Data.IntMap as I
import System.Random

The obvious solution is to simply sort the list and take the first 100 elements. Sorting, however, usually takes O(n log n), which is not allowed. Fortunately, by using the square of the distance rather than the distance we not only save a million square root operations, but it also makes the value we’re sorting by an integer, which allows us to use an IntMap that has O(1) insertion and thus O(n) sorting.

closest :: Int -> [(Int, Int)] -> [(Int, Int)]
closest n = take n . concat . I.elems . I.fromListWith (flip (++)) .
            map (\(x,y) -> (x*x+y*y, [(x,y)]))

To test, we need a million random points.

points :: IO [(Int, Int)]
points = take 1000000 <$> liftA2 zip (randomRs (-1000, 1000) <$> newStdGen)
                                     (randomRs (-1000, 1000) <$> newStdGen)

Finally we run the algorithm.

main :: IO ()
main = print . closest 100 =<< points

To see whether our algorithm is truly linear, let’s look at some timings:

1 million: 2.9 s
2 million: 5.7 s
4 million: 11.6 s
8 million: 23.8 s

Looks fairly linear to me.