String R中从字符串中任何位置查找模式的算法/代码

String R中从字符串中任何位置查找模式的算法/代码,string,r,loops,pattern-matching,String,R,Loops,Pattern Matching,我想从任何给定字符串中的任何位置找到模式,以便模式至少重复一个阈值次数。 例如,对于字符串“a0cc0vaaaabaaa00bvw”,模式应该是“aaaab”。另一个示例:对于字符串“FF00F0000”,模式应为“0f”。 在这两种情况下,阈值均为3,即模式应至少重复3次 如果有人能在R中提出一个优化的方法来解决这个问题,请与我分享。目前,我通过使用3个嵌套循环来实现这一点,这需要很多时间 谢谢 没有优化(即使它很快)函数,但我认为这是一种更为简单的方法 获取确定长度>阈值的所有模式:使用ma

我想从任何给定字符串中的任何位置找到模式,以便模式至少重复一个阈值次数。 例如,对于字符串“a0cc0vaaaabaaa00bvw”,模式应该是“aaaab”。另一个示例:对于字符串“FF00F0000”,模式应为“0f”。 在这两种情况下,阈值均为3,即模式应至少重复3次

如果有人能在R中提出一个优化的方法来解决这个问题,请与我分享。目前,我通过使用3个嵌套循环来实现这一点,这需要很多时间

谢谢

没有优化(即使它很快)函数,但我认为这是一种更为简单的方法

  • 获取确定长度>阈值的所有模式:使用
    mapply
    substr
  • 获取这些模式的出现次数并提取出现次数最大的模式:使用
    str\u locate\u all
    进行矢量化
  • 对所有长度重复1-2次,并选择出现次数最多的长度
  • 这是我的密码。我正在创建两个函数(步骤1-2)和步骤3:

    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_path
    find.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