1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | -- -- Problem: Find the 51000000000-th character of the string (wordNumber Infinity) -- where a wordNumber is defined as -- -- wordNumber 1 = "one" -- wordNumber 2 = "onetwo" -- wordNumber 3 = "onetwothree" -- wordNumber 15 = "onetwothreefourfivesixseveneightnineteneleventwelvethirteenfourteenfifteen" -- ... -- -- The answer should be presented as ( sum of all numbers up to that point -- , the 51000000000-th character -- ) {-# LANGUAGE BangPatterns #-} import Debug.Trace import Data.Int import Control.Monad import Data.Array.Unboxed ones = ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"] tens = ["", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"] teens = ["ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"] lenOnes, lenTens, lenTeens :: UArray Int64 Int64 lenOnes = listArray (0,9) $ [0,3,3,5,4,5,3,5,5,4] -- "", "one","two", ... lenTens = listArray (0,9) $ [0,3,6,6,5,5,5,7,6,6] lenTeens = listArray (0,9) $ [3,6,6,8,8,7,7,9,8,8] -- first element is "ten" 3 -- potentially cleaner version -- but I feared Haskell might do surprising things behind my -- back so I stuck with the above -- lenBelowHundred = listArray (0,99) $ map (fromIntegral . length . wordify) [1..99] -- wordify 123 = "onehundredtwentythree" -- This is only used once in presenting the final result character wordify :: Int64 -> String wordify n | n < 10 = ones !! fromIntegral n | n < 20 = teens !! (fromIntegral n-10) | n < 100 = splitterTen | n < 1000 = splitter 100 "hundred" | n < 1000000 = splitter 1000 "thousand" | n < 1000000000 = splitter 1000000 "million" where splitterTen = let (t, x) = n `divMod` 10 in (tens !! fromIntegral t) ++ wordify x splitter div suffix = let (t, x) = n `divMod` div in (wordify t) ++ suffix ++ wordify x -- Optimized version of length (wordify n) -- Used in number crunching wordLength n = wordLength' 0 n -- Tail recursive version wordLength' :: Int64 -> Int64 -> Int64 wordLength' !pad !n | n < 10 = lenOnes ! n + pad | n < 20 = lenTeens ! (n-10) + pad | n < 100 = splitterTen | n < 1000 = splitter 100 7 | n < 1000000 = splitter 1000 8 | otherwise = splitter 1000000 7 where splitterTen = let !(!t, !x) = n `divMod` 10 in wordLength' (lenTens ! t + pad) x splitter !d !suffix = let !(!t, !x) = n `divMod` d in wordLength' (wordLength' (suffix+pad) t) x -- Tail recursive solve :: Int64 -> (Int64, Int64, Int64) -> [Int64] -> (Int64, Int64, Int64) solve !n !acc@(!sumNum, !sumLen, !curr) (!num:nums) | sumLen' >= n = (sumNum', sumLen, num) | otherwise = solve n (sumNum', sumLen', num) nums where sumNum' = sumNum + num sumLen' = sumLen + wordLength num solution :: Int64 -> (Int64, Char) solution !x = let (sumNum, sumLen, n) = solve x (0,0,1) [1..] in (sumNum, (wordify n) !! (fromIntegral $ x - sumLen - 1)) main = do print $ solution 1234 -- Make sure we are sane print $ solution 51000000000 |
LS0KLS0gUHJvYmxlbTogRmluZCB0aGUgNTEwMDAwMDAwMDAtdGggY2hhcmFjdGVyIG9mIHRoZSBzdHJpbmcgKHdvcmROdW1iZXIgSW5maW5pdHkpCi0tIHdoZXJlIGEgd29yZE51bWJlciBpcyBkZWZpbmVkIGFzCi0tCi0tIHdvcmROdW1iZXIgMSA9ICJvbmUiCi0tIHdvcmROdW1iZXIgMiA9ICJvbmV0d28iCi0tIHdvcmROdW1iZXIgMyA9ICJvbmV0d290aHJlZSIKLS0gd29yZE51bWJlciAxNSA9ICJvbmV0d290aHJlZWZvdXJmaXZlc2l4c2V2ZW5laWdodG5pbmV0ZW5lbGV2ZW50d2VsdmV0aGlydGVlbmZvdXJ0ZWVuZmlmdGVlbiIKLS0gLi4uCi0tCi0tIFRoZSBhbnN3ZXIgc2hvdWxkIGJlIHByZXNlbnRlZCBhcyAoIHN1bSBvZiBhbGwgbnVtYmVycyB1cCB0byB0aGF0IHBvaW50Ci0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIHRoZSA1MTAwMDAwMDAwMC10aCBjaGFyYWN0ZXIKLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCnstIyBMQU5HVUFHRSBCYW5nUGF0dGVybnMgIy19CgppbXBvcnQgRGVidWcuVHJhY2UKaW1wb3J0IERhdGEuSW50CmltcG9ydCBDb250cm9sLk1vbmFkCmltcG9ydCBEYXRhLkFycmF5LlVuYm94ZWQKCm9uZXMgPSBbIiIsICJvbmUiLCAidHdvIiwgInRocmVlIiwgImZvdXIiLCAiZml2ZSIsICJzaXgiLCAic2V2ZW4iLCAiZWlnaHQiLCAibmluZSJdCnRlbnMgPSBbIiIsICJ0ZW4iLCAidHdlbnR5IiwgInRoaXJ0eSIsICJmb3J0eSIsICJmaWZ0eSIsICJzaXh0eSIsICJzZXZlbnR5IiwgImVpZ2h0eSIsICJuaW5ldHkiXQp0ZWVucyA9IFsidGVuIiwgImVsZXZlbiIsICJ0d2VsdmUiLCAidGhpcnRlZW4iLCAiZm91cnRlZW4iLCAiZmlmdGVlbiIsICJzaXh0ZWVuIiwgInNldmVudGVlbiIsICJlaWdodGVlbiIsICJuaW5ldGVlbiJdCgpsZW5PbmVzLCBsZW5UZW5zLCBsZW5UZWVucyA6OiBVQXJyYXkgSW50NjQgSW50NjQKbGVuT25lcyA9IGxpc3RBcnJheSAoMCw5KSAkIFswLDMsMyw1LDQsNSwzLDUsNSw0XSAtLSAiIiwgIm9uZSIsInR3byIsIC4uLgpsZW5UZW5zID0gbGlzdEFycmF5ICgwLDkpICQgWzAsMyw2LDYsNSw1LDUsNyw2LDZdCmxlblRlZW5zID0gbGlzdEFycmF5ICgwLDkpICQgWzMsNiw2LDgsOCw3LDcsOSw4LDhdIC0tIGZpcnN0IGVsZW1lbnQgaXMgInRlbiIgMwoKLS0gcG90ZW50aWFsbHkgY2xlYW5lciB2ZXJzaW9uCi0tIGJ1dCBJIGZlYXJlZCBIYXNrZWxsIG1pZ2h0IGRvIHN1cnByaXNpbmcgdGhpbmdzIGJlaGluZCBteSAKLS0gYmFjayBzbyBJIHN0dWNrIHdpdGggdGhlIGFib3ZlCi0tIGxlbkJlbG93SHVuZHJlZCA9IGxpc3RBcnJheSAoMCw5OSkgJCBtYXAgKGZyb21JbnRlZ3JhbCAuIGxlbmd0aCAuIHdvcmRpZnkpIFsxLi45OV0KCi0tIHdvcmRpZnkgMTIzID0gIm9uZWh1bmRyZWR0d2VudHl0aHJlZSIKLS0gVGhpcyBpcyBvbmx5IHVzZWQgb25jZSBpbiBwcmVzZW50aW5nIHRoZSBmaW5hbCByZXN1bHQgY2hhcmFjdGVyCndvcmRpZnkgOjogSW50NjQgLT4gU3RyaW5nCndvcmRpZnkgbgogICAgfCBuIDwgMTAgICAgICAgICA9IG9uZXMgISEgZnJvbUludGVncmFsIG4KICAgIHwgbiA8IDIwICAgICAgICAgPSB0ZWVucyAhISAoZnJvbUludGVncmFsIG4tMTApCiAgICB8IG4gPCAxMDAgICAgICAgID0gc3BsaXR0ZXJUZW4KICAgIHwgbiA8IDEwMDAgICAgICAgPSBzcGxpdHRlciAxMDAgImh1bmRyZWQiCiAgICB8IG4gPCAxMDAwMDAwICAgID0gc3BsaXR0ZXIgMTAwMCAidGhvdXNhbmQiCiAgICB8IG4gPCAxMDAwMDAwMDAwID0gc3BsaXR0ZXIgMTAwMDAwMCAibWlsbGlvbiIKICAgIHdoZXJlCiAgICAgICAgc3BsaXR0ZXJUZW4gPSBsZXQgKHQsIHgpID0gbiBgZGl2TW9kYCAxMAogICAgICAgICAgICAgICAgICAgICAgaW4gKHRlbnMgISEgZnJvbUludGVncmFsIHQpICsrIHdvcmRpZnkgeAogICAgICAgIHNwbGl0dGVyIGRpdiBzdWZmaXggPSBsZXQgKHQsIHgpID0gbiBgZGl2TW9kYCBkaXYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW4gKHdvcmRpZnkgdCkgKysgc3VmZml4ICsrIHdvcmRpZnkgeAoKLS0gT3B0aW1pemVkIHZlcnNpb24gb2YgbGVuZ3RoICh3b3JkaWZ5IG4pCi0tIFVzZWQgaW4gbnVtYmVyIGNydW5jaGluZwp3b3JkTGVuZ3RoIG4gPSB3b3JkTGVuZ3RoJyAwIG4KCi0tIFRhaWwgcmVjdXJzaXZlIHZlcnNpb24Kd29yZExlbmd0aCcgOjogSW50NjQgLT4gSW50NjQgLT4gSW50NjQKd29yZExlbmd0aCcgIXBhZCAhbgogICAgfCBuIDwgMTAgICAgICAgICA9IGxlbk9uZXMgISBuICsgcGFkCiAgICB8IG4gPCAyMCAgICAgICAgID0gbGVuVGVlbnMgISAobi0xMCkgKyBwYWQKICAgIHwgbiA8IDEwMCAgICAgICAgPSBzcGxpdHRlclRlbgogICAgfCBuIDwgMTAwMCAgICAgICA9IHNwbGl0dGVyIDEwMCA3CiAgICB8IG4gPCAxMDAwMDAwICAgID0gc3BsaXR0ZXIgMTAwMCA4CiAgICB8IG90aGVyd2lzZSAgICAgID0gc3BsaXR0ZXIgMTAwMDAwMCA3CiAgICB3aGVyZQogICAgICAgIHNwbGl0dGVyVGVuID0gbGV0ICEoIXQsICF4KSA9ICBuIGBkaXZNb2RgIDEwCiAgICAgICAgICAgICAgICAgICAgICBpbiB3b3JkTGVuZ3RoJyAobGVuVGVucyAhIHQgKyBwYWQpIHgKICAgICAgICBzcGxpdHRlciAhZCAhc3VmZml4ID0gbGV0ICEoIXQsICF4KSA9IG4gYGRpdk1vZGAgZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbiB3b3JkTGVuZ3RoJyAod29yZExlbmd0aCcgKHN1ZmZpeCtwYWQpIHQpIHgKCi0tIFRhaWwgcmVjdXJzaXZlCnNvbHZlIDo6IEludDY0IC0+IChJbnQ2NCwgSW50NjQsIEludDY0KSAtPiBbSW50NjRdIC0+IChJbnQ2NCwgSW50NjQsIEludDY0KQpzb2x2ZSAhbiAhYWNjQCghc3VtTnVtLCAhc3VtTGVuLCAhY3VycikgKCFudW06bnVtcykKICAgIHwgc3VtTGVuJyA+PSBuID0gKHN1bU51bScsIHN1bUxlbiwgbnVtKQogICAgfCBvdGhlcndpc2UgPSBzb2x2ZSBuIChzdW1OdW0nLCBzdW1MZW4nLCBudW0pIG51bXMKICAgIHdoZXJlCiAgICAgICAgc3VtTnVtJyA9IHN1bU51bSArIG51bQogICAgICAgIHN1bUxlbicgPSBzdW1MZW4gKyB3b3JkTGVuZ3RoIG51bQoKc29sdXRpb24gOjogSW50NjQgLT4gKEludDY0LCBDaGFyKQpzb2x1dGlvbiAheCA9CiAgICBsZXQgKHN1bU51bSwgc3VtTGVuLCBuKSA9IHNvbHZlIHggKDAsMCwxKSBbMS4uXQogICAgaW4gKHN1bU51bSwgKHdvcmRpZnkgbikgISEgKGZyb21JbnRlZ3JhbCAkIHggLSBzdW1MZW4gLSAxKSkKCm1haW4gPSBkbwogICAgcHJpbnQgJCBzb2x1dGlvbiAxMjM0IC0tIE1ha2Ugc3VyZSB3ZSBhcmUgc2FuZQogICAgcHJpbnQgJCBzb2x1dGlvbiA1MTAwMDAwMDAwMAoK


