String R中从字符串中任何位置查找模式的算法/代码
我想从任何给定字符串中的任何位置找到模式,以便模式至少重复一个阈值次数。 例如,对于字符串“a0cc0vaaaabaaa00bvw”,模式应该是“aaaab”。另一个示例:对于字符串“FF00F0000”,模式应为“0f”。 在这两种情况下,阈值均为3,即模式应至少重复3次 如果有人能在R中提出一个优化的方法来解决这个问题,请与我分享。目前,我通过使用3个嵌套循环来实现这一点,这需要很多时间 谢谢 没有优化(即使它很快)函数,但我认为这是一种更为简单的方法String R中从字符串中任何位置查找模式的算法/代码,string,r,loops,pattern-matching,String,R,Loops,Pattern Matching,我想从任何给定字符串中的任何位置找到模式,以便模式至少重复一个阈值次数。 例如,对于字符串“a0cc0vaaaabaaa00bvw”,模式应该是“aaaab”。另一个示例:对于字符串“FF00F0000”,模式应为“0f”。 在这两种情况下,阈值均为3,即模式应至少重复3次 如果有人能在R中提出一个优化的方法来解决这个问题,请与我分享。目前,我通过使用3个嵌套循环来实现这一点,这需要很多时间 谢谢 没有优化(即使它很快)函数,但我认为这是一种更为简单的方法 获取确定长度>阈值的所有模式:使用ma
mapply
和substr
str\u locate\u all
进行矢量化library(stringr)
ss = "ff00f0f0f0f0f0f0f0f0000"
ss <- "a0cc0vaaaabaaaabaaaabaa00bvw"
find_pattern_length <-
function(length=1,ss){
patt = mapply(function(x,y) substr(ss,x,y),
1:(nchar(ss)-length),
(length+1):nchar(ss))
res = str_locate_all(ss,unique(patt))
ll = unlist(lapply(res,length))
list(patt = patt[which.max(ll)],
rep = max(ll))
}
get_pattern_threshold <-
function(ss,threshold =3 ){
res <-
sapply(seq(threshold,nchar(ss)),find_pattern_length,ss=ss)
res[,which.max(res['rep',])]
}
这是我的解决方案,它没有经过优化(使用
模式构建向量),因为您需要至少三次重复,所以有一个很好的O(n^2)方法
对于每个可能的模式长度d
将字符串切割成长度d
的部分。如果d=5
,则为:
a0cc0
vaaaa
baaaa
baaaa
baa00
bvw
现在看一下后面的每一对字符串A[k]
和A[k+1]
。如果它们相等,那么至少有两个重复的模式。然后进一步(k+2
,k+3
)等等。最后还要检查A[k-1]
的后缀和A[k+n]
的前缀是否合适(其中,k+n
是不匹配的第一个字符串)
从某个上限(最多n/3
)开始,对每个d
重复该步骤
你有n/3
可能的长度,然后n/d
长度d
的字符串来检查每个d
。它应该给出复杂性O(n(n/d)d)=O(n^2)
可能不是最优的,但我发现这个裁剪的想法非常简洁;)使用正则表达式,这是为这类东西设计的。可能有更优化的方法,但就易于编写代码而言,很难击败它。数据:
vec <- c("a0cc0vaaaabaaaabaaaabaa00bvw","ff00f0f0f0f0f0f0f0f0000")
请注意,阈值为3时,第二个字符串的实际最长模式为0f0f,而不是0f(在阈值5时恢复为0f)。为此,我使用了反向引用(\\1
),并根据需要重复这些操作以达到阈值。然后我需要substr
结果,因为令人恼火的是,在使用与perl兼容的正则表达式时,base R没有一种简单的方法来获取捕获的子表达式。可能有一种不太难的方法可以做到这一点,但substr方法在本次考试中效果很好普尔
此外,根据@G.Grothendieck的回答中的讨论,这里是模式长度上限的版本,它只是添加了limit参数和对regexp的轻微修改
find_rep_path <- function(vec, reps, limit) {
regexp <- paste0(c("(.{1,", limit,"})", rep("\\1", reps - 1L)), collapse="")
match <- regmatches(vec, regexpr(regexp, vec, perl=T))
substr(match, 1, nchar(match) / reps)
}
sapply(vec, find_rep_path, reps=3L, limit=3L)
# a0cc0vaaaabaaaabaaaabaa00bvw ff00f0f0f0f0f0f0f0f0000
# "a" "0f"
find_rep_pathfind.string
查找最大长度的子串,但必须遵守以下条件:(1)子串必须连续重复至少th次,并且(2)子串长度不得超过len
reps <- function(s, n) paste(rep(s, n), collapse = "") # repeat s n times
find.string <- function(string, th = 3, len = floor(nchar(string)/th)) {
for(k in len:1) {
pat <- paste0("(.{", k, "})", reps("\\1", th-1))
r <- regexpr(pat, string, perl = TRUE)
if (attr(r, "capture.length") > 0) break
}
if (r > 0) substring(string, r, r + attr(r, "capture.length")-1) else ""
}
修改了本应测试find.string2
的James Joyce测试,该测试实际上使用了find.string
。这一问题现在已得到解决。用于有界模式(即不太大)我认为最好先创建所有可能的子字符串,然后对它们进行计数。这就是子模式是否可以重叠。如果不重叠,则更改循环中的步骤
pat="a0cc0vaaaabaaaabaaaabaa00bvw"
len=nchar(pat)
thr=3
reps=floor(len/2)
# all poss strings up to half length of pattern
library(stringr)
pat=str_split(pat, "")[[1]][-1]
str.vec=vector()
for(win in 2:reps)
{
str.vec= c(str.vec, rollapply(data=pat,width=win,FUN=paste0, collapse=""))
}
# the max length string repeated more than 3 times
tbl=table(str.vec)
tbl=tbl[tbl>=3]
tbl[which.max(nchar(names(tbl)))]
aaaabaa
3
注意:虽然我很懒,在循环中附加/增长str.vec
,但对于一个更大的问题,如果你想计算出来,我很确定str.vec
的实际长度是由模式的长度预先确定的。在第一个示例中,模式ab
也符合条件。你在寻找最大重复字符串吗“?@carl:是的!我在寻找最大重复字符串。因此,在第一个示例中,“a”
将是答案?我计算了14个“a”
。不,“a”不会是第一个示例的答案。而且重复应该是连续的。这非常令人困惑。正如@SimonO'Hanlon所说,模式“a”
重复四次,在“aaaab”
重复三次之前发生。它达到了您连续重复三次(超过)的阈值,那么为什么不“aaaa”
qualify。显然,您还有一个未声明的要求,即重复元素必须超过一个字符……您能解释一下您的代码吗?根据发布的问题,答案应该是“aaaab”不是全部答案。我认为您希望得到重复次数至少达到阈值的最大长度字符串。重要的是字符串的长度,而不是重复次数。只要字符串重复3次或更多次,就包括在内。然后,长度最长的字符串将获胜(如果我理解正确,这并不能保证!)+1真的很酷的解决方案。regexing的巧妙使用。这看起来非常高效(至少在我看来).Side.comment,find_rep_path
code实际上已经矢量化了,所以您不需要使用sapply,但是如果这样做,当没有匹配项时,结果向量的长度可能与输入的长度不同。使用sapply,您会得到一个包含所有名称和匹配项的列表,如第二个示例所示。@BrodieG我正在尝试获取模式允许1个字符的抖动。例如:对于字符串“a0cc0vaaaabaadbaabbaa00bvw”,模式应该是“aaajb”,其中“j”可以是任何东西。你能建议修改上述模式查找代码吗
find_rep_path <- function(vec, reps) {
regexp <- paste0(c("(.+)", rep("\\1", reps - 1L)), collapse="")
match <- regmatches(vec, regexpr(regexp, vec, perl=T))
substr(match, 1, nchar(match) / reps)
}
sapply(vec, find_rep_path, reps=3L)
# a0cc0vaaaabaaaabaaaabaa00bvw ff00f0f0f0f0f0f0f0f0000
# "aaaab" "0f0f"
sapply(vec, find_rep_path, reps=5L)
# $a0cc0vaaaabaaaabaaaabaa00bvw
# character(0)
#
# $ff00f0f0f0f0f0f0f0f0000
# [1] "0f"
find_rep_path <- function(vec, reps, limit) {
regexp <- paste0(c("(.{1,", limit,"})", rep("\\1", reps - 1L)), collapse="")
match <- regmatches(vec, regexpr(regexp, vec, perl=T))
substr(match, 1, nchar(match) / reps)
}
sapply(vec, find_rep_path, reps=3L, limit=3L)
# a0cc0vaaaabaaaabaaaabaa00bvw ff00f0f0f0f0f0f0f0f0000
# "a" "0f"
reps <- function(s, n) paste(rep(s, n), collapse = "") # repeat s n times
find.string <- function(string, th = 3, len = floor(nchar(string)/th)) {
for(k in len:1) {
pat <- paste0("(.{", k, "})", reps("\\1", th-1))
r <- regexpr(pat, string, perl = TRUE)
if (attr(r, "capture.length") > 0) break
}
if (r > 0) substring(string, r, r + attr(r, "capture.length")-1) else ""
}
> find.string("a0cc0vaaaabaaaabaaaabaa00bvw")
[1] "aaaab"
> find.string("ff00f0f0f0f0f0f0f0f0000")
[1] "0f0f"
>
> joyce <- readLines("http://www.gutenberg.org/files/4300/4300-8.txt")
> joycec <- paste(joyce, collapse = " ")
> system.time(result <- find.string2(joycec, len = 25))
user system elapsed
1.36 0.00 1.39
> result
[1] " Hoopsa boyaboy hoopsa!"
find.string2 <- function(string, th = 3, len = floor(nchar(string)/th)) {
pat <- paste0(c("(.", "{1,", len, "})", rep("\\1", th-1)), collapse = "")
r <- regexpr(pat, string, perl = TRUE)
ifelse(r > 0, substring(string, r, r + attr(r, "capture.length")-1), "")
}
> find.string2("a0cc0vaaaabaaaabaaaabaa00bvw")
[1] "aaaab"
> find.string2("ff00f0f0f0f0f0f0f0f0000")
[1] "0f0f"
> system.time(result <- find.string2(joycec, len = 25))
user system elapsed
0 0 0
> result
[1] "w"
pat="a0cc0vaaaabaaaabaaaabaa00bvw"
len=nchar(pat)
thr=3
reps=floor(len/2)
# all poss strings up to half length of pattern
library(stringr)
pat=str_split(pat, "")[[1]][-1]
str.vec=vector()
for(win in 2:reps)
{
str.vec= c(str.vec, rollapply(data=pat,width=win,FUN=paste0, collapse=""))
}
# the max length string repeated more than 3 times
tbl=table(str.vec)
tbl=tbl[tbl>=3]
tbl[which.max(nchar(names(tbl)))]
aaaabaa
3