【问题标题】:F# Count how Many times a substring Contains within a stringF#计算子字符串在字符串中包含多少次
【发布时间】:2017-03-16 01:44:53
【问题描述】:

如何计算一个字符串中子字符串存在的次数?

我的意思是,如果你有一个字符串 "one, two, three, one, one, two",你怎么能把它算作 "one" 出现 3 次?

我认为String.Contains 可以完成这项工作,但它只检查子字符串是否存在。 String.forall 用于字符,因此没有选项。

所以我真的完全停在了这里。有的可以启发我吗?

【问题讨论】:

  • 我会选择一个答案,但根据您的 cmets,对于哪个是最佳解决方案似乎存在争议。所以我在这里有点进退两难。不过,我真的很感谢您的所有解决方案。给了我很多思考。

标签: string count f# contains


【解决方案1】:

这是一个遍历字符串的简单实现,使用String.IndexOf 跳到下一个出现的子字符串,并计算它成功的次数。

let substringCount (needle : string) (haystack : string) =
    let rec loop count (index : int) =
        if index >= String.length haystack then count
        else
            match haystack.IndexOf(needle, index) with
            | -1 -> count
            | idx -> loop (count + 1) (idx + 1)
    if String.length needle = 0 then 0 else loop 0 0

请注意,这会计算重叠出现次数,例如 subtringCount "aa" "aaaa" = 3。如果您想要不重叠,只需将idx + 1 替换为idx + String.length needle

【讨论】:

    【解决方案2】:

    您可以使用Regex.Escape 将您要搜索的字符串转换为正则表达式,然后使用正则表达式函数:

    open System.Text.RegularExpressions
    
    let countMatches wordToMatch (input : string) =
        Regex.Matches(input, Regex.Escape wordToMatch).Count
    

    测试:

    countMatches "one" "one, two, three, one, one, two"
    // Output: 3
    

    【讨论】:

      【解决方案3】:

      我的一个同学提出了迄今为止我见过的最简单的解决方案。

      let countNeedle (haystack :string) (needle : string) =
            match needle with
            | "" -> 0
            | _ -> (haystack.Length - haystack.Replace(needle, "").Length) / needle.Length
      

      【讨论】:

      • 请注意,这无法捕获重叠的情况,并且需要分配新字符串 - 这不太可能成为问题,但需要考虑。
      • 最简单最快的是正则表达式。这种情况是三种情况中最慢的,因为它处理所有字符,生成一个临时字符串(其他情况不这样做),然后执行长度计算。
      • @Panagiotis 正则表达式绝对是最短的,但不是最快的——使用完整的正则表达式引擎很少会像更简单的算法一样快。在我的快速测试中,String.IndexOf 方法在许多小输入循环上的速度是两倍,在搜索长字符串时速度是 10 倍。
      • @JakeLishman 1) 仅当您的方法 生成任何临时字符串时。仅在这种情况下,这是可能的。字符串拆分也可能看起来既快速又简单,但临时字符串的数量和垃圾收集成本导致执行速度慢了 10 倍以上。
      • @JakeLishman 2) IndexOf 是一个 OS 函数的包装器。这几乎是作弊——在那个级别你可以使用很多优化,甚至使用 SIMD 操作在一个操作中比较多个字节
      【解决方案4】:

      创建要搜索的字符串的尾部序列,即锚定在其末尾的所有子字符串切片。然后,您可以使用forall 功能来确定每个匹配项的开头的匹配数​​。它只是比(fun s -> s.StartsWith needle) 打高尔夫球。

      let count needle haystack =
          [ for i in 0..String.length haystack - 1 -> haystack.[i..] ]
          |> Seq.filter (Seq.forall2 (=) needle)
          |> Seq.length
      
      count "aba" "abacababac"
      // val it : int = 3
      

      【讨论】:

        【解决方案5】:
        // This approach assumes the data is comma-delimited.
        let data = "one, two, three, one, one, two"
        let dataArray = data.Split([|','|]) |> Array.map (fun x -> x.Trim())
        let countSubstrings searchTerm = dataArray |> Array.filter (fun x -> x = searchTerm) |> Array.length
        let countOnes = countSubstrings "one"
        
        let data' = "onetwothreeoneonetwoababa"
        
        // This recursive approach makes no assumptions about a delimiter,
        // and it will count overlapping occurrences (e.g., "aba" twice in "ababa").
        // This is similar to Jake Lishman's answer.
        let rec countSubstringFromI s i what = 
            let len = String.length what
            if i + len - 1 >= String.length s then 0
            else (if s.Substring(i, len) = what then 1 else 0) + countSubstringFromI s (i + 1) what
        
        let countSubStrings' = countSubstringFromI data' 0 "one"
        

        【讨论】:

          猜你喜欢
          • 2023-03-22
          • 1970-01-01
          • 2021-12-11
          • 2013-02-05
          • 2016-06-01
          • 2012-12-06
          • 2020-02-21
          • 2012-02-12
          相关资源
          最近更新 更多