单个字符排列

时间:2016-05-16 11:53:36

标签: list haskell permutation

我一直在试用Haskell已经有一段时间了,我发现了一个有趣的问题:

我必须以非常特别的方式生成字符串的所有排列:

假设我有这个字符串:

let input = "_h_i_"

我想在input中得到所有下划线的排列,保持其他字符的实际顺序,如下所示:

let results = ["hi___", "h_i__", "h__i_", "h___i", "_hi__", "_h_i_", "_h__i", "__hi_", "__h_i", "___hi"] 

我不知道如何做到这一点。

3 个答案:

答案 0 :(得分:1)

执行此操作的一种方法是生成所有排列,但只保留那些保留非下划线字符顺序的排列。

import Data.List

orderedPermutationsOf :: Char -> String -> [String]
orderedPermutationsOf c s =
  let except = filter (/= c)
  in nub $ filter (\x -> except s == except x) $ permutations s

它并不是非常高效,但它可以完成工作。

答案 1 :(得分:1)

so :: String -> [String]
so = takeWhile' notDone . iterate shift . preproccess
  where    
    preproccess xs = (replicate (uCount xs) '_') ++ (letters xs)
    uCount = length . filter (=='_')
    letters = filter (/='_')
    notDone = not . and . map (== '_') . dropWhile (/='_')

takeWhile' :: (a -> Bool) -> [a] -> [a]
takeWhile' _ [] = []
takeWhile' f (x:xs) 
  | f x = x:(takeWhile' f xs)
  | otherwise = [x]

shift :: String -> String
shift [] = []
shift (x:xs)
  | x /= '_' = roll lead [] rest
  | otherwise = roll [] [] (x:xs)
  where     
    (lead,rest) = span (/='_') (x:xs)
    roll _ acc [] = acc
    roll _ acc [a] = acc++[a]
    roll ins acc (a:b:bs)
      | (a == '_') && (b /= '_') = acc++ins++(b:a:bs)
      | otherwise = roll ins (acc++[a]) (b:bs)

可能不是最漂亮的代码,但似乎有效。该算法基本上与您手动执行的算法相同,但反之亦然。

shift通过滚动字符串并将最左边的非下划线字符向左移动来工作。重复此操作,直到所有非下划线字符都在左侧。

预处理只是为了获得他留下的所有下划线以及右边的所有其他字符。

答案 2 :(得分:1)

这是另一种解决方案

u='_'

perms :: String -> Int -> [String]
perms x 0 = [x]
perms [x] n = rotations $ x: (replicate n u)
perms (x:xs) n = (map (x:) $ perms xs n) ++ if(x/=u) then perms (u:x:xs) (n-1) else []

rotate xs n = zipWith const (drop n (cycle xs)) xs
rotations x = map (rotate x) [1..length x]

我更改了输入签名,用作

> perms "hi" 3
["h___i","h__i_","h_i__","hi___","_h__i","_h_i_","_hi__","__h_i","__hi_","___hi"]

我认为有一个更简单的解决方案隐藏在那里,但现在没有足够的时间。