-- Rhyme.hs
module Main where
import Data.Char
import Data.List
import Data.Array
import Data.Function
import Data.List.Split
import Data.String.Utils
import System.IO
import BestMatching
testInput = "Боже! думает бедняк,\nБедный Ваня еле дышит,\nВесь в поту, от страха бледный,\nВ темноте пред ним собака\n(Вы представьте Вани злость!)\nВаня стал; - шагнуть не может.\nГоре! малый я не сильный;\nЕсли сам земли могильной\nКрасногубый вурдалак.\nКто-то кость, ворча, грызёт.\nНа могиле гложет кость.\nПо могилам; вдруг он слышит, -\nРаз он позднею порой,\nСпотыкаясь, чуть бредёт\nСъест упырь меня совсем,\nТрусоват был Ваня бедный:\nЧрез кладбище шёл домой.\nЧто же? вместо вурдалака -\nЭто, верно, кости гложет\nЯ с молитвою не съем."
testInputLines = splitOn "\n" testInput
editDistance xs ys = table ! (m, n)
where
(m, n) = (length xs, length ys)
x = array (1, m) (zip [1..] xs)
y = array (1, n) (zip [1..] ys)
table = array bnds [(ij, dist ij) | ij <- range bnds]
bnds = ((0, 0), (m, n))
dist (0, j) = j
dist (i, 0) = i
dist (i, j) = minimum [table ! (i - 1, j) + 1, table ! (i, j - 1) + 1,
if x ! i == y ! j then table ! (i - 1, j - 1) else 1 + table ! (i - 1, j - 1)]
transcription = transcription' . (' ':) . filter (`elem` ' ':'\n':'ё':['а'..'я']) . map toLower
where
rules = [
("ьё", "'jо"), ("ье", "'jэ"), ("ья", "'jа"), ("ью", "'jу"), ("ъё", "jо"), ("ъе", "jэ"), ("ъя", "jа"), (" ю", "jу"), (" ё", "jо"), (" е", "jэ"),
(" я", "jа"), (" ю", "jу"), ("ё", "'о"), ("е", "'э"), ("я", "'а"), ("ю", "'у"), ("й", "j"), ("ъ", ""), ("ь", "'"), ("жи", "жы"), ("ши", "шы")
]
transcription' s = foldl (\ s (src, dst) -> replace src dst s) s rules
syllableCount = length . filter (`elem` "ёуеыаоэяию") . map toLower
groupBySyllableCount = map (map fst) . groupBy ((==) `on` snd) . sortBy (compare `on` snd) . map (\ s -> (s, syllableCount s))
transcriptionEndingLength = 5
getRhymablePart = take transcriptionEndingLength . filter (`notElem` " ") . reverse . transcription
orderByRhyme = filter (\ (s1, s2) -> s1 < s2) . rhymeMatcher
where
rhymeMatcher lines = findBestMatching (\ s1 s2 -> if s1 == s2 then 1000 else (editDistance `on` getRhymablePart) s1 s2) lines lines
solve = concatGroups [] . reorder . map process . splitOn "\n"
where
process line = (line, getRhymablePart line, syllableCount line)
groupLines = map (map (\ (x, _, _) -> x)) . sortBy (compare `on` length) . groupBy (\ (_, _, x1) (_, _, x2) -> x1 == x2) . sortBy (\ (_, _, x1) (_, _, x2) -> compare x2 x1)
reorder = map reorderGroup . map (map reorderRhyme) . map orderByRhyme . groupLines
reorderRhyme = id -- not implemented
reorderGroup = id -- not implemented
concatGroups acc [] = acc
concatGroups acc (group:[]) = acc ++ (group >>= (\ (a, b) -> [a, b]))
concatGroups acc (group1:group2:rest) = concatGroups (acc ++ (zip group1 group2 >>= (\ ((a, b), (c, d)) -> [a, c, b, d]))) rest
main =
hSetEncoding stdin utf8 >> hSetEncoding stdout utf8 >> getContents >>= mapM putStrLn . solve
-- BestMatching.hs
module BestMatching (findBestMatching) where
data NetworkEdge = NetworkEdge { from :: Int, to :: Int, cost :: Int, capacity :: Int, flow :: Int }
deriving (Show)
infinity = 100000
initNetwork distance xs ys partSize source sink part1Offset part2Offset =
[0..partSize - 1] >>= (\ v -> [NetworkEdge source (part1Offset + v) 0 1 0, NetworkEdge (part2Offset + v) sink 0 1 0]
++ [NetworkEdge (part1Offset + v) (part2Offset + v') (distance (xs !! v) (ys !! v')) 1 0 | v' <- [0..partSize - 1]])
>>= (\ e@(NetworkEdge x y d _ _) -> [e, NetworkEdge y x (-d) 0 0])
maxExtraFlowOnPath result path i network
| path !! i == -1 = result
| otherwise = maxExtraFlowOnPath (min result $ capacity edge - flow edge) path (from edge) network
where edge = network !! (path !! i)
addFlowOnPath extraFlow path i network
| path !! i == -1 = network
| (path !! i) `mod` 2 == 0 =
let (x, y:y':ys) = splitAt (path !! i) network in
addFlowOnPath extraFlow path (from y) $ x ++ ((y { flow = flow y + extraFlow }):(y' { flow = flow y' - extraFlow }):ys)
| otherwise =
let (x, y':y:ys) = splitAt (path !! i - 1) network in
addFlowOnPath extraFlow path (from y) $ x ++ (y' { flow = flow y' - extraFlow }):((y { flow = flow y + extraFlow }):ys)
addExtraFlowOnPath path i network = addFlowOnPath (maxExtraFlowOnPath infinity path i network) path i network
findShortestPathInResidualNetwork networkSize = findShortestPathInResidualNetwork' networkSize ([-1,-1..], 0:[infinity, infinity..]) networkSize
where
findShortestPathInResidualNetwork' 0 acc _ _ to _ = ((fst acc) !! to /= -1, take networkSize $ fst acc)
findShortestPathInResidualNetwork' iteration acc networkSize from to network =
findShortestPathInResidualNetwork' (iteration - 1) (foldl relax acc $ zip [0..] network) networkSize from to network
relax acc@(result, distances) (i, edge)
| flow edge >= capacity edge || distances !! (from edge) >= infinity || distances !! (to edge) <= (distances !! (from edge)) + cost edge = acc
| otherwise =
let (resPrev, res:resRest) = splitAt (to edge) result in
let (distPrev, dist:distRest) = splitAt (to edge) distances in
(resPrev ++ (i:resRest), distPrev ++ ((distances !! (from edge)) + (cost edge):distRest))
findBestMatching distance xs ys
| length xs /= length ys = error "Lengths should be equal"
| otherwise = restoreAnswer $ addFlowWhilePossible True [-1, -1..] $ initNetwork distance xs ys partSize source sink part1Offset part2Offset
where
partSize = length xs
source = 0
sink = 1 + partSize * 2
part1Offset = 1
part2Offset = 1 + partSize
networkSize = 2 + partSize * 2
addFlowWhilePossible False _ network = network
addFlowWhilePossible True path network =
let (hasPath, nextPath) = findShortestPathInResidualNetwork networkSize source sink network in
addFlowWhilePossible hasPath nextPath $ addExtraFlowOnPath path sink network
restoreAnswer network = filter (\ (NetworkEdge f t _ _ fl) -> fl == 1 && f >= part1Offset && f < part1Offset + partSize && t >= part2Offset && t < part2Offset + partSize && f < t) network
>>= (\ (NetworkEdge f t _ _ _) -> [(xs !! (f - part1Offset), ys !! (t - part2Offset))])
Comments