利用@RichardScrivenadist
可以使用的洞察力(它计算“近似字符串距离”。我制作了一个更全面的函数。请注意"trafos"
代表用于确定两个字符串之间“距离”的“转换”(底部示例)
编辑这个答案可能会产生错误/意外的结果;正如@wdkrnls 所指出的:
我对“apple”和“big apple bagels”运行了你的函数,它返回了“appl”。我会期待“苹果”。
有关错误结果,请参阅下面的说明。我们从一个获取longest_string
列表的函数开始:
longest_string <- function(s){return(s[which.max(nchar(s))])}
然后我们可以使用@RichardSriven 的工作和stringi
库:
library(stringi)
lcsbstr <- function(a,b) {
sbstr_locations<- stri_locate_all_regex(drop(attr(adist(a, b, counts=TRUE), "trafos")), "M+")[[1]]
cmn_sbstr<-stri_sub(longest_string(c(a,b)), sbstr_locations)
longest_cmn_sbstr <- longest_string(cmn_sbstr)
return(longest_cmn_sbstr)
}
或者我们可以重写我们的代码以避免使用任何外部库(仍然使用 R 的原生adist
函数):
lcsbstr_no_lib <- function(a,b) {
matches <- gregexpr("M+", drop(attr(adist(a, b, counts=TRUE), "trafos")))[[1]];
lengths<- attr(matches, 'match.length')
which_longest <- which.max(lengths)
index_longest <- matches[which_longest]
length_longest <- lengths[which_longest]
longest_cmn_sbstr <- substring(longest_string(c(a,b)), index_longest , index_longest + length_longest - 1)
return(longest_cmn_sbstr )
}
上述两个函数都只标识'hello '
为最长的公共子字符串,而不是 ' hello r'
(无论哪个参数是两者中较长的一个):
identical('hello',
lcsbstr_no_lib('hello', 'hello there'),
lcsbstr( 'hello', 'hello there'),
lcsbstr_no_lib('hello there', 'hello'),
lcsbstr( 'hello there', 'hello'))
最后编辑
请注意此结果的一些奇怪行为:
lcsbstr('hello world', 'hello')
#[1] 'hell'
我期待着'hello'
,但是由于转换实际上(通过删除)将 w o rld 中的“o”移动到 hell o中的“o” ——根据以下条件,只有hell部分被认为是匹配的M
:
drop(attr(adist('hello world', 'hello', counts=TRUE), "trafos"))
#[1] "MMMMDDDMDDD"
#[1] vvvv v
#[1] "hello world"
使用这个 Levenstein 工具可以观察到这种行为——它提供了两种可能的解决方案,相当于这两种转换
#[1] "MMMMDDDMDDD"
#[1] "MMMMMDDDDDD"
我不知道我们是否可以配置adist
为首选一种解决方案而不是另一种解决方案?(变换具有相同的“权重”——相同数量的“M”和“D”——不知道如何选择具有更多连续 M
数的变换)
最后,别忘了 adist 允许你传入ignore.case = TRUE
(FALSE
默认)
"trafos"
财产的钥匙adist
;从一个字符串到另一个字符串的“转换”:
M
转换序列作为返回值的“trafos”属性返回,作为带有元素、和的字符串I
,表示匹配、插入、删除和替换D
S