F#:测试两个字符串是否为字谜

时间:2015-03-25 16:02:44

标签: f#

我是编程新手,F#是我的第一语言。

这是我的代码:

let areAnagrams (firstString: string) (secondString: string) =
    let countCharacters (someString: string) =
        someString.ToLower().ToCharArray() |> Array.toSeq
                                           |> Seq.countBy (fun eachChar -> eachChar)
                                           |> Seq.sortBy (snd >> (~-))
    countCharacters firstString = countCharacters secondString

let testString1 = "Laity"
let testString2 = "Italy"
printfn "It is %b that %s and %s are anagrams." (areAnagrams testString1 testString2) (testString1) (testString2)

这是输出:

  

Laity和意大利是字谜,这是错误的。

出了什么问题?我应该做些什么改变?

2 个答案:

答案 0 :(得分:4)

countCharacters的实现仅使用第二个元素(每个字符的出现次数)对元组进行排序,但如果有多个字符出现的次数相同,则不会定义顺序。

如果您对两个样本运行countCharacters函数,则可以看到问题:

> countCharacters "Laity";;
val it : seq<char * int> = seq [('l', 1); ('a', 1); ('i', 1); ('t', 1); ...]
> countCharacters "Italy";;
val it : seq<char * int> = seq [('i', 1); ('t', 1); ('a', 1); ('l', 1); ...]

一种解决方案是只使用Seq.sort并使用字母代码和出现次数对元组进行排序。

另一个问题是你正在比较两个seq<_>值并且这不使用结构比较,因此你需要将结果转换为列表或数组(完全评估的内容) :

let countCharacters (someString: string) =
    someString.ToLower().ToCharArray() 
    |> Seq.countBy (fun eachChar -> eachChar)
    |> Seq.sort
    |> List.ofSeq

请注意,您实际上并不需要Seq.countBy - 因为如果您只是对所有字符进行排序,它将同样有效(重复的字符将是一个接一个)。所以你可以使用:

let countCharacters (someString: string) =
    someString.ToLower() |> Seq.sort |> List.ofSeq

答案 1 :(得分:1)

对两个字符串的字符进行排序为您提供了一个简单的解决方案,但这可能是一个很好的递归示例。

您可以立即排除不同长度的字符串。

您还可以通过将每个迭代替换为空字符串来过滤掉所有出现的char。

let rec areAnagram (x:string) (y:string) =
    if x.Lenght  <> t.Lenght 
    then false else
    if x.Lenght = 0
    then true else 
    let reply = x.[0].ToString ()
    areAnagram
        (x.Replace (reply,""))
        (y.Replace (reply,""))

上述应该比许多用例的排序更快。

无论如何,我们可以进一步将其转换为快速整数排序,而无需递归和字符串替换

let inline charToInt c = 
    int c - int '0'
let singlePassAnagram (x:string) =
    let hash : int array = Array.zeroCreate 100
    x |> Seq.iter (fun c->
    hash.[charToInt c] <- (hash.[charToInt c]+1)
    )  
let areAnagramsFast 
        (x:string) (y:string) =
    if x.Length <> y.Length
    then false else
    (singlePassAnagram x) = 
        (singlePassAnagram y)

这是fiddle