【问题标题】:Codewars - upper and lowercase letters are considered the same character - Ruby 2.5Codewars - 大写和小写字母被认为是相同的字符 - Ruby 2.5
【发布时间】:2022-11-18 03:18:45
【问题描述】:

所以我在这个 Kata 上:

`

def  first_non_repeating_letter(s) 
  a = s.chars
  a.select! { |char| a.count(char) == 1 }
  if a.empty?
    ("")
  else
    a.first
  end
end

`

我唯一缺少的是:

“作为一个额外的挑战,大写和小写字母被认为是相同的字符,但函数应该返回正确的情况 对于首字母.例如,输入‘sTreSS’应该返回‘T’。”

s.downcase.chars 在这里不适用。我试过 .casecmp 但仍然不成功。我应该使用正则表达式吗?

【问题讨论】:

    标签: ruby methods


    【解决方案1】:

    您的实现有点棘手,因为现在您没有任何显式比较操作。相反,您使用 Array#count 的技巧。

    问题是这个实现不仅不灵活,而且效率很低.它的 O(n^2) (因为对于 select 遍历的每个元素,你在整个数组上调用 count),所以对于足够大的输入 你的实施将是极其减缓。

    好消息是解决性能问题你也可以轻松实现这个额外的挑战(因为比较操作将变得明确,所以你将能够只缩小需要缩小的部分而不会影响输入本身).

    让我们考虑一下通用解决方案。什么是“不重复”字符?这是一个角色字符串中的第一次和最后一次出现相同.因此,如果我们遍历字符串并构建一些辅助数据结构,a) 保留第一次/最后一次出现,b) 允许其恒定时间查找,我们可以在线性时间内解决任务。

    那我们走吧:

    def  first_non_repeating_letter(str) 
      # We'd like to have a hash where keys are chars (downcased)
      # and values are arrays of [<first occurence index>, <last occurence index>]
      positions = {}
    
      str.each_char.with_index do |c, i|
        key = c.downcase
    
        if positions.key?(key)
          # We've seen it before, so all we need to do is to update its last occurrence position
          positions[key][1] = i
        else
          # This is the 1st time we met the char `c`. So we make its first
          # and last occurrence positions the same (i) 
          positions[key] = [i, i]
        end
      end
    
      # At this point, for the given string 'sTreSS' (for example) we would build
      # positions = {"s"=>[0, 5], "t"=>[1, 1], "r"=>[2, 2], "e"=>[3, 3]}
    
      # Now let's do the main job finally
      str.chars.find { |c| positions[c.downcase][0] == positions[c.downcase][1] } || ""
    end
    
    pry(main)> first_non_repeating_letter('sTreSS')
    => "T"
    

    【讨论】:

      【解决方案2】:

      如果给定的字符串是s,计算复杂度显然至少为 O(s.size)(因为我们需要检查整个字符串以确认给定字符在字符串中恰好出现一次)。因此,我们可能会寻找一种具有相同计算复杂度的方法,最好是使用相对高效的 Ruby 内置方法并且易于理解和测试的方法。

      假设给定的字符串如下:

      s = "TgHEtGgh"
      

      假设不考虑大小写,第一个只出现一次的字符是"E"

      作为第一步,我们可能希望计算字符串中每个字符的频率,小写和大写字符被同等对待1个:

      sdn = s.downcase
        #=> "tghetggh"
      enum = sdn.each_char 
        #=> #<Enumerator: "tghetggh":each_char>
      h = enum.tally
        #=> {"t"=>2, "g"=>3, "h"=>2, "e"=>1}
      

      这使用方法String#downcaseString#each_charEnumerable#tally。其中每一种方法的计算复杂度都是O(s.size),所以h的计算复杂度是一样的。作为奖励,这些方法中的每一个都是用 C 语言实现的。

      当然,我们可以链接这些方法:

      h = s.downcase.each_char.tally
        #=> {"t"=>2, "g"=>3, "h"=>2, "e"=>1}
      

      我们现在可以简单地遍历字符串 s 的字符,直到找到一个字符 ch[c.downcase] == 1true

      s.each_char.find { |c| h[c.downcase] == 1 }
        #=> "E"
      

      参见Enumerable#find

      为了使最后一步的计算复杂度为 O(s.size),计算 h[c.downcase] 的计算复杂度必须等于 1。事实上,哈希键查找的计算复杂度略大于1,但从实用的角度来看,我们可以假设它等于1

      1. 请注意,通过先写arr = sdn.chars #=&gt; ["t", "g", "h", "e", "t", "g", "g", "h"],然后写h = arr.tally,我们可以获得相同的结果。这样做的缺点是,与 String#each_char 不同,String#chars 创建一个临时数组,消耗内存,但在这种情况下,使用 each_char 节省的内存可能是最小的。

      【讨论】:

        猜你喜欢
        • 2013-02-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-08
        • 1970-01-01
        • 1970-01-01
        • 2022-06-30
        • 2021-01-06
        相关资源
        最近更新 更多