1 
{-
  2 
 
  3 
Pearl 2: Surpassing
  4 
 
  5 
Definitions:
  6 
A surpasser of an element of an array (list) is a greater element to the right.
  7 
x[j] is a surpasser of x[i] if i<j and x[i]<x[j].
  8 
The surpasser count of an element is the number of its surpassers.
  9 
 
 10 
Problem:
 11 
Compute the maximum surpasser count of an array of length n>1 in an O(n log n) algorithm.
 12 
 
 13 
Solution:
 14 
 
 15 
Note 1: In the imperative programming, using iterative+binary search.
 16 
We will use Divide and Conquer
 17 
 
 18 
Note 2: Cannot do better than O(n log n), b/c this is equivalen to sorting (you will see:
 19 
the table we create)
 20 
-}
 21 
module P2 where
 22 
 
 23 
-- Test word is:         GENERATION  
 24 
-- Surpasser count is:   5625140100 
 25 
-- So the maximum isL    6
 26 
testX = "GENERATION" :: [Char]
 27 
 
 28 
-- Maximum Surpasser Count
 29 
-- Brute force approach
 30 
-- Requires O(N^2) steps
 31 
-- Goes on shrinking list of length n: n+(n-1)+(n-2)+.. = n^2/2
 32 
msc         ::     Ord a => [a] -> Int
 33 
msc xs        =    maximum [scount z zs | z:zs <- tails xs]
 34 
scount x xs    =     length (filter (x<) xs)
 35 
 
 36 
-- We'll redfine tails, to make sure empty returns empty
 37 
tails             :: [a] -> [[a]]
 38 
tails []        = []
 39 
tails (x:xs)    = (x:xs):tails xs
 40 
 
 41 
-- Divide and Conquer
 42 
-- The trick is to find some 'data' that we can join together
 43 
-- easily. Then we can divide, and then join.
 44 
-- There are a few tricks to get to this 'join':
 45 
-- 1. Define data structure that contains enough information. Then you can join them.
 46 
--    This data structure is the table, that contains not only the Maximum Surpasser from
 47 
--    each divide, but all the counts!
 48 
-- 2. If we keep the Table in sorted order, the join can be done easily! (in Linear time)
 49 
 
 50 
table [x]        =    [(x,0)]        -- one element
 51 
table xs        =    join' (m-n) (table ys)  (table zs)
 52 
                    where
 53 
                        m = length xs
 54 
                        n = m `div` 2
 55 
                        (ys,zs) = splitAt n xs
 56 
 
 57 
join' 0 txs []    = txs
 58 
join' n []  tys    = tys
 59 
join' n txs@((x,c):txs') tys@((y,d):tys')
 60 
        | x<y     = (x,c+n) : join' n txs' tys   
 61 
        | x>=y     = (y,d) : join' (n-1) txs tys'
 62 
-- the lists are ordered within themselves.
 63 
-- so if x<y ==> All the rest (and there's length n of tys) are also
 64 
-- greater than x ==> we should add this count!
 65 
-- if x>y, we just put y.
 66 
-- if x==y, we just want to make sure we continue in reduced manner, so we put (y,d), remmove
 67 
-- it from the next join, and then move on to continue.
 68 
 
 69 
-- Main
 70 
 
 71 
main = do
 72 
    putStr "Brute force       : "
 73 
    print $ msc testX
 74 
    putStr "Divide and Conquer: "
 75 
    print $ maximum $ map (snd) (table testX)