如何在 Ruby 中统计一个字符串中另一个字符串元素的出现次数?

3
如何检查字符串中某个短语出现的次数?
例如,假设该短语是“donut”。
str1 = "I love donuts!"
#=> returns 1 because "donuts" is found once.
str2 = "Squirrels do love nuts" 
#=> also returns 1 because of 'do' and 'nuts' make up donut
str3 = "donuts do stun me" 
#=> returns 2 because 'donuts' and 'do stun' has all elements to make 'donuts'

我查看了这个SO,它建议使用include,但只有在donuts按顺序拼写时才有效。
我想到了这个方法,但它并不能在拼写完"donuts"的所有元素后停止。例如:"I love donuts" #=> ["o", "d", "o", "n", "u", "t", "s"]
def word(arr)
  acceptable_word = "donuts".chars
  arr.chars.select { |name| acceptable_word.include? name.downcase }
end

我该如何检查给定字符串中出现了多少次donuts?没有边际情况。输入总是String,不为nil。如果它仅包含donut元素,则不应计为1次出现;它需要包含donuts,不必按顺序。


2
一个可能是重复的问题,链接为https://dev59.com/GF8e5IYBdhLWcg3wPYhA - Zepplock
这个问题与上面的问题不是重复的,因为在这里,“do stun”匹配“donuts”,例如,不要求子字符串匹配。 - Aleksei Matiushkin
虽然不同,但我在我的帖子中指出了另一个stackoverflow链接:https://dev59.com/DWsy5IYBdhLWcg3w3h6I,其中字符串的“排序”并不重要。也许“顺序”不是一个好描述。对于混淆感到抱歉!正如 @mudasobwa所说,donutsdo stun 都应该返回匹配。 - Iggy
4个回答

5

代码

def count_em(str, target)
  target.chars.uniq.map { |c| str.count(c)/target.count(c) }.min
end

示例

count_em "I love donuts!", "donuts"                      #=> 1
count_em "Squirrels do love nuts", "donuts"              #=> 1
count_em "donuts do stun me", "donuts"                   #=> 2
count_em "donuts and nuts sound too delicious", "donuts" #=> 3
count_em "cats have nine lives", "donuts"                #=> 0
count_em "feeding force scout", "coffee"                 #=> 1
count_em "feeding or scout", "coffee"                    #=> 0

str = ("free mocha".chars*4).shuffle.join
  # => "hhrefemcfeaheomeccrmcre eef oa ofrmoaha "
count_em str, "free mocha"
  #=> 4

说明

对于

str = "feeding force scout"
target = "coffee"

a = target.chars
  #=> ["c", "o", "f", "f", "e", "e"] 
b = a.uniq
  #=> ["c", "o", "f", "e"] 
c = b.map { |c| str.count(c)/target.count(c) }
  #=> [2, 2, 1, 1] 
c.min
  #=> 1 

在计算c时,考虑传递给块的b的第一个元素,并将其分配给块变量c
c = "c"

那么块计算就是

d = str.count(c)
  #=> 2 
e = target.count(c)
  #=> 1
d/e
  #=> 2

这表明str包含足够的"c"来匹配两次"coffee"。

获得c的其余计算类似。

补充说明

如果与target相匹配的str字符必须与target字符的顺序相同,则可以使用以下正则表达式。

target = "coffee"

r = /#{ target.chars.join(".*?") }/i
  #=> /c.*?o.*?f.*?f.*?e.*?e/i

matches = "xcorr fzefe yecaof tfe erg eeffoc".scan(r)
  #=> ["corr fzefe ye", "caof tfe e"]
matches.size
  #=> 2

"feeding force scout".scan(r).size
  #=> 0 

正则表达式中的问号是必需的,以使搜索变为非贪婪模式。

这个代码非常简洁,适用于像“甜甜圈”这样每个字母只出现一次的短语,但是在像“咖啡”这样有重复字母的术语中会失效。 “免费摩卡”是否应该匹配它? - tadman

2
解决方案比较简单(使用map(&:dup)可以避免输入的突变):
pattern = 'donuts'
[str1, str2, str3].map(&:dup).map do |s|
  loop.with_index do |_, i|
    break i unless pattern.chars.all? { |c| s.sub!(c, '') }
  end
end
#⇒ [1, 1, 2]

你的解决方案总是令人难以置信。 - Aleksey

1
这里有两种方法,一种要求字母按顺序出现,另一种则不考虑顺序。在两种情况下,每个字母的频率都得到了尊重,因此"coffee"必须匹配两个"f"和两个"e"字母,"free mocha"不足以匹配,缺少第二个"f"。
def sorted_string(string)
  string.split('').sort.join
end

def phrase_regexp_sequence(phrase)
  Regexp.new(
    phrase.downcase.split('').join('.*')
  )
end

def phrase_regexp_unordered(phrase)
  Regexp.new(
    phrase.downcase.gsub(/\W/, '').split('').sort.chunk_while(&:==).map do |bit|
      "#{bit[0]}{#{bit.length}}"
    end.join('.*')
  )
end

def contains_unordered(phrase, string)
  !!phrase_regexp_unordered(phrase).match(sorted_string(string.downcase))
end

def contains_sequence(phrase, string)
  !!phrase_regexp_sequence(phrase).match(string.downcase)
end

strings = [
  "I love donuts!",
  "Squirrels do love nuts",
  "donuts do stun me",
  "no stunned matches",
]

phrase = 'donut'

strings.each do |string|
  puts '%-30s %s %s' % [
    string,
    contains_unordered(phrase, string),
    contains_sequence(phrase, string)
  ]
end

# => I love donuts!                 true true
# => Squirrels do love nuts         true true
# => donuts do stun me              true true
# => no stunned matches             true false

0

简单解决方案:

criteria = "donuts"
str1 = "I love donuts!"
str2 = "Squirrels do love nuts"
str3 = "donuts do stun me"

def strings_construction(criteria, string)
    unique_criteria_array = criteria.split("").uniq
    my_hash = {}

    # Let's count how many times each character of the string matches a character in the string 
    unique_criteria_array.each do |char|
        my_hash[char] ? my_hash[char] = my_hash[char] + 1 : my_hash[char] = string.count(char)
    end

    my_hash.values.min
end

puts strings_construction(criteria, str1) #=> 1
puts strings_construction(criteria, str2) #=> 1
puts strings_construction(criteria, str3) #=> 2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接