找到所有回文单词对

时间:2015-03-07 22:12:22

标签: haskell

我提出了一个不真实的问题:在词汇表中找到所有回文单词对,所以我在下面写了解决方案,

import Data.List

findParis :: Ord a => [[a]] -> [[[a]]]

findPairs ss =
  filter ((== 2) . length) 
    . groupBy ((==) . reverse) 
    . sortBy (compare . reverse) 
    $ ss

main = do
  print . findPairs . permutations $ ['a'..'c'] 

-- malfunctioning: only got partial results [["abc","cba"]]
-- expected: [["abc","cba"],["bac","cab"],["bca","acb"]]

如果值得尝试,你可以帮忙纠正吗?

@Solution

受益于@David Young @chi评论,调整后的工作代码如下:

import Data.List (delete)
import Data.Set hiding (delete, map)

findPairs :: Ord a => [[a]] -> [([a], [a])]

findPairs ss =
  let
    f [] = []
    f (x : xs) =
      let y = reverse x
      in
        if x /= y
        then
          let ss' = delete y xs
          in (x, y) : f ss'
        else f xs
  in
    f . toList
      . intersection (fromList ss)
      $ fromList (map reverse ss)

2 个答案:

答案 0 :(得分:2)

import Data.List
import Data.Ord

-- find classes of equivalence by comparing canonical forms (CF)
findEquivalentSets :: Ord b => (a->b) -> [a] -> [[a]]
findEquivalentSets toCanonical =

      filter ((>=2) . length)                            -- has more than one
                                                         -- with the same CF?

    . groupBy ((((== EQ) .) .) (comparing toCanonical))  -- group by CF

    . sortBy (comparing toCanonical)                     -- compare CFs

findPalindromes :: Ord a => [[a]] -> [[[a]]]
findPalindromes = findEquivalentSets (\x -> min x (reverse x))

只要我们可以为我们的元素分配一些有效可计算的规范形式(CF),这个函数就可以找到很多种等价。

当寻找回文对时,如果一个是另一个的反向,则两个字符串是等价的。 CF是字典上较小的字符串。

findAnagrams :: Ord a => [[a]] -> [[[a]]]
findAnagrams = findEquivalentSets sort

在这个例子中,如果一个是另一个的字谜,则两个字符串是等价的。 CF是排序字符串(香蕉→aaabnn)。

同样我们可以找到SOUNDEX等价物等等。

这不是非常有效,因为需要在每次比较时计算CF.我们可以以可读性为代价来缓存它。

findEquivalentSets :: Ord b => (a->b) -> [a] -> [[a]]
findEquivalentSets toCanonical =

          map (map fst)                               -- strip CF

        . filter ((>=2) . length)                     -- has more than one
                                                      -- with the same CF?

        . groupBy ((((== EQ) .) .) (comparing snd))   -- group by CF

        . sortBy (comparing snd)                      -- compare CFs

        . map (\x -> (x, toCanonical x))              -- pair the element with its CF

答案 1 :(得分:1)

这是您可能想要考虑的方法。

使用sort意味着有一些键控函数word2key可以为回文对的两个单词产生相同的值。我想到的第一个是

word2key w = min w (reverse w)

因此,将键控函数映射到单词列表,排序,按等号分组,取长度为2的组,然后从键中恢复两个单词(使用键等于单词或反过来。

为了清楚起见,写了几个本地定义,给出了:

findPals :: (Ord a, Eq a) => [[a]] -> [[[a]]]
findPals = map (key2words . head) .
           filter ((== 2) . length) .
           groupBy (==) .
           sort .
           (map word2key)
  where word2key w = min w (reverse w)
        key2words k = [k, reverse k]

修改

我在一个陈旧的窗口中发布了我的答案而没有刷新,所以错过了从上午那里得到的非常好的回复。上方。

Mea culpa

所以我要提到两个答案都是众所周知的变体(在Perl圈子中)“Schwartzian transform”,它本身应用了一个共同的数学模式 - h = f' . g . f - 翻译一个任务到任务更容易的替代表示,完成工作,然后转换回原始表示。

Schwartzian变换使用相应的键对值进行元组化,按键排序,然后将原始值从键/值元组中拉回。

我上面提到的小黑客是基于key2wordsword2key的非确定性反向关系的事实。只有当两个单词具有相同的密钥时才有效,但这正是问题中的情况,并且由filter保险。

overAndBack :: (Ord b, Eq c) => (a -> b) -> ([b] -> [c]) -> (c -> d) -> [a] -> [d]
overAndBack f g f' = map f' . g . sort . map f

findPalPairs :: (Ord a, Eq a) => [[a]] -> [[[a]]]
findPalPairs = overAndBack over just2 back
  where over w = min w (reverse w)
        just2  = filter ((== 2) . length) . groupBy (==)
        back   = (\k -> [k, reverse k]) . head

哪个演示为

*Main> findPalPairs $ words "I saw no cat was on a chair"
[["no","on"],["saw","was"]]

感谢您提出的好问题。