假设我有一个名字词典(一个巨大的CSV文件)。我想从一个没有明显可解析点(。, - ,_)的电子邮件中猜出一个名字。我想做这样的事情:
dict = ["sam", "joe", "john", "parker", "jane", "smith", "doe"]
word = "johnsmith"
x = 0
y = word.length-1
name_array = []
for i in x..y
match_me = word[x..i]
dict.each do |name|
if match_me == name
name_array << name
end
end
end
name_array
# => ["john"]
不错,但我想要“约翰史密斯”或[“约翰”,“史密斯”]
换句话说,我递归循环遍历单词(即未解析的电子邮件字符串,“johndoe@gmail.com”),直到我在字典中找到匹配项。 我知道:这是非常低效的。如果有一个更容易的方法来做到这一点,我全都耳朵!
如果没有更好的方法,那么告诉我如何解决上面的例子,因为它有两个主要缺陷:(1)如何设置循环的长度(参见查找“i”的问题)下面),以及(2)如何在上面的例子中增加“x”,以便我可以在给定任意字符串的情况下循环遍历所有可能的字符组合?
找到循环长度的问题,“i”:
for an arbitrary word, how can we derive "i" given the pattern below?
for a (i = 1)
a
for ab (i = 3)
a
ab
b
for abc (i = 6)
a
ab
abc
b
bc
c
for abcd (i = 10)
a
ab
abc
abcd
b
bc
bcd
c
cd
d
for abcde (i = 15)
a
ab
abc
abcd
abcde
b
bc
bcd
bcde
c
cd
cde
d
de
e
答案 0 :(得分:5)
r = /^(#{Regexp.union(dict)})(#{Regexp.union(dict)})$/
word.match(r)
=> #<MatchData "johnsmith" 1:"john" 2:"smith">
正则表达式可能需要一些时间来构建,但它的速度非常快。
答案 1 :(得分:3)
我敢提出一个不太优雅的蛮力解决方案,但仍然有用
由于我的英语不好,我无法找出一个可以分成多个方式的长个人姓名,所以让我们分析一个短语:
word = "godisnowhere"
字典:
@dict = [ "god", "is", "now", "here", "nowhere", "no", "where" ]
@lengths = @dict.collect {|w| w.length }.uniq.sort
数组@lengths
为算法添加了一些优化,我们将使用它来修剪字典中不存在的长度的子词,而不实际执行字典查找。数组已排序,这是另一种优化。
解决方案的主要部分是递归函数,它在给定的单词中查找初始子词并重新启动尾部子词。
def find_head_substring(word)
# boundary condition:
# remaining subword is shorter than the shortest word in @dict
return [] if word.length < @lengths[0]
splittings = []
@lengths.each do |len|
break if len > word.length
head = word[0,len]
if @dict.include?(head)
tail = word[len..-1]
if tail.length == 0
splittings << head
else
tails = find_head_substring(tail)
unless tails.empty?
tails.collect!{|tail| "#{head} #{tail}" }
splittings.concat tails
end
end
end
end
return splittings
end
现在看看它是如何运作的
find_head_substring(word)
=>["god is no where", "god is now here", "god is nowhere"]
我没有对它进行过广泛的测试,所以我提前道歉:)
答案 2 :(得分:2)
如果你只想要字典中的匹配点击:
dict.select{ |r| word[/#{r}/] }
=> ["john", "smith"]
您存在太多令人困惑的子命令的风险,因此您可能希望对字典进行排序,以便首先使用更长的名称:
dict.sort_by{ |w| -w.size }.select{ |r| word[/#{r}/] }
=> ["smith", "john"]
你仍然会遇到这样的情况:较长的名字后面有一个较短的子串并获得多次点击,因此你需要找到一种方法来清除它们。你可以有一个名字数组和另一个姓氏,并且每个都有第一个返回的扫描结果,但考虑到名字和姓氏的多样性,这不能保证100%的准确性,并且仍会收集一些糟糕的结果。
如果没有关于此人姓名的代码的进一步提示,这种问题就没有真正好的解决方案。也许扫描信息的正文以表示敬意或告诫部分会有所帮助。
答案 3 :(得分:0)
我不确定你在做什么,但不是那么简单:
dict.each do |first|
dict.each do |last|
puts first,last if first+last == word
end
end
答案 4 :(得分:0)
这个包装出现了所有情况,不一定是两个:
pattern = Regexp.union(dict)
matches = []
while match = word.match(pattern)
matches << match.to_s # Or just leave off to_s to keep the match itself
word = match.post_match
end
matches