【问题标题】:Create all combinations of letter substitution in string在字符串中创建所有字母替换组合
【发布时间】:2019-02-11 12:46:21
【问题描述】:

我有一个字符串“ECET”,我想创建所有可能的字符串,其中我用“X”替换一个或多个字母(除第一个之外的所有字母)。

所以在这种情况下,我的结果是:

> result
[1] "EXET" "ECXT" "ECEX" "EXXT" "EXEX" "ECXX" "EXXX"

关于如何解决这个问题的任何想法?

这不仅仅是创建“X”的可能组合/排列,还包括如何将它们与现有字符串组合。

【问题讨论】:

    标签: r combinations


    【解决方案1】:

    使用combnFUN 参数:

    a <- "ECET"
    
    fun <- function(n, string) {
      combn(nchar(string), n, function(x) {
        s <- strsplit(string, '')[[1]]
        s[x] <- 'X'
        paste(s, collapse = '')
      } )
    }
    lapply(seq_len(nchar(a)), fun, string = a)
    
    [[1]]
    [1] "XCET" "EXET" "ECXT" "ECEX"
    
    [[2]]
    [1] "XXET" "XCXT" "XCEX" "EXXT" "EXEX" "ECXX"
    
    [[3]]
    [1] "XXXT" "XXEX" "XCXX" "EXXX"
    
    [[4]]
    [1] "XXXX"
    

    unlist 获取单个向量。可能有更快的解决方案。

    保持第一个字符不变:

    paste0(
      substring(a, 1, 1),
      unlist(lapply(seq_len(nchar(a) - 1), fun, string = substring(a, 2)))
    )
    
    [1] "EXET" "ECXT" "ECEX" "EXXT" "EXEX" "ECXX" "EXXX"
    

    【讨论】:

      【解决方案2】:

      这是一个递归解决方案:

      f <- function(x,pos=2){
        if(pos <= nchar(x))
          c(f(x,pos+1), f(`substr<-`(x, pos, pos, "X"),pos+1))
        else x
      }
      f(x)[-1]
      # [1] "ECEX" "ECXT" "ECXX" "EXET" "EXEX" "EXXT" "EXXX"
      

      或者使用expand.grid

      do.call(paste0, expand.grid(c(substr(x,1,1),lapply(strsplit(x,"")[[1]][-1], c, "X"))))[-1]
      # [1] "EXET" "ECXT" "EXXT" "ECEX" "EXEX" "ECXX" "EXXX"
      

      或者使用combn/Reduce/substr&lt;-:

      combs <- unlist(lapply(seq(nchar(x)-1),combn, x =seq(nchar(x))[-1],simplify = F),F)
      sapply(combs, Reduce, f= function(x,y) `substr<-`(x,y,y,"X"), init = x)
      # [1] "EXET" "ECXT" "ECEX" "EXXT" "EXEX" "ECXX" "EXXX"
      

      解释第二个解决方案

      pairs0 <- lapply(strsplit(x,"")[[1]][-1], c, "X") # pairs of original letter + "X"
      pairs1 <- c(substr(x,1,1), pairs0)                # including 1st letter (without "X")
      do.call(paste0, expand.grid(pairs1))[-1]          # expand into data.frame and paste
      

      【讨论】:

        【解决方案3】:

        有点为了使用二进制逻辑添加另一个选项:

        假设您的字符串总是 4 个字符长:

        input<-"ECET"
        invec <- strsplit(input,'')[[1]]
        sapply(1:7, function(x) {
          z <- invec
          z[rev(as.logical(intToBits(x))[1:4])] <- "X"
          paste0(z,collapse = '')
        })
        
        [1] "ECEX" "ECXT" "ECXX" "EXET" "EXEX" "EXXT" "EXXX"
        

        如果字符串必须更长,您可以用 2 的幂计算值,应该这样做:

        input<-"ECETC"
        pow <- nchar(input)
        invec <- strsplit(input,'')[[1]]
        sapply(1:(2^(pow-1) - 1), function(x) {
          z <- invec
          z[rev(as.logical(intToBits(x))[1:(pow)])] <- "X"
          paste0(z,collapse = '')
        })
        
        [1] "ECETX" "ECEXC" "ECEXX" "ECXTC" "ECXTX" "ECXXC" "ECXXX" "EXETC" "EXETX" "EXEXC" "EXEXX" "EXXTC" "EXXTX" "EXXXC"
        [15] "EXXXX"
        

        这个想法是知道可能改变的数量,它是 3 个位置的二进制,所以 2^3 减去 1,因为我们不想保留无替换字符串:7

        intToBits 返回整数的二进制值,为 5:

        > intToBits(5)
         [1] 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        

        R 默认使用 32 位,但是我们只想要一个与我们的字符串长度对应的逻辑向量,所以我们只保留原始字符串的 nchar。 然后我们转换为逻辑并反转这 4 个布尔值,因为我们永远不会触发最后一位(4 个字符为 8),它永远不会为真:

        > intToBits(5)
         [1] 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        > tmp<-as.logical(intToBits(5)[1:4])
        > tmp
        [1]  TRUE FALSE  TRUE FALSE
        > rev(tmp)
        [1] FALSE  TRUE FALSE  TRUE
        

        为了避免覆盖我们的原始向量,我们将它复制到 z 中,然后使用这个逻辑向量替换 z 中的位置。

        为了得到一个不错的输出,我们返回 paste0 并折叠为空,以重新创建单个字符串并检索字符向量。

        【讨论】:

          【解决方案4】:

          另一个带有combn的版本,使用purrr:

          s <- "ECET"
          f <- function(x,y) {substr(x,y,y) <- "X"; x}
          g <- function(x) purrr::reduce(x,f,.init=s)
          unlist(purrr::map(1:(nchar(s)-1), function(x) combn(2:nchar(s),x,g)))
          
          #[1] "EXET" "ECXT" "ECEX" "EXXT" "EXEX" "ECXX" "EXXX"
          

          或没有咕噜声:

          s <- "ECET"
          f <- function(x,y) {substr(x,y,y) <- "X"; x}
          g <- function(x) Reduce(f,x,s)
          unlist(lapply(1:(nchar(s)-1),function(x) combn(2:nchar(s),x,g)))
          

          【讨论】:

            【解决方案5】:

            这是一个基本的 R 解决方案,但我觉得它很复杂,有 3 个嵌套循环。

            replaceChar <- function(x, char = "X"){
              n <- nchar(x)
              res <- NULL
              for(i in seq_len(n)){
                cmb <- combn(n, i)
                r <- apply(cmb, 2, function(cc){
                  y <- x
                  for(k in cc)
                    substr(y, k, k) <- char
                  y
                })
                res <- c(res, r)
              }
              res
            }
            
            x <- "ECET"
            
            replaceChar(x)
            replaceChar(x, "Y")
            replaceChar(paste0(x, x))
            

            【讨论】:

              【解决方案6】:

              带有布尔索引的矢量化方法:

              permX <- function(text, replChar='X') {
                  library(gtools)
                  library(stringr)  
                  # get TRUE/FALSE permutations for nchar(text)
                  idx <- permutations(2, nchar(text),c(T,F), repeats.allowed = T)
              
                  # we don't want the first character to be replaced
                  idx <- idx[1:(nrow(idx)/2),]
              
                  # split string into single chars
                  chars <- str_split(text,'')
              
                  # build data.frame with nrows(df) == nrows(idx)
                  df = t(data.frame(rep(chars, nrow(idx))))
              
                  # do replacing
                  df[idx] <- replChar
              
                  row.names(df) <- c()
                  return(df)
              }
              permX('ECET')
              
              [,1] [,2] [,3] [,4]  
              [1,] "E"  "C"  "E"  "T"   
              [2,] "E"  "C"  "E"  "X"  
              [3,] "E"  "C"  "X"  "T"  
              [4,] "E"  "C"  "X"  "X"  
              [5,] "E"  "X"  "E"  "T"  
              [6,] "E"  "X"  "E"  "X"  
              [7,] "E"  "X"  "X"  "T"  
              [8,] "E"  "X"  "X"  "X"  
              

              【讨论】:

                【解决方案7】:

                一个更简单的解决方案

                # expand.grid to get all combinations of the input vectors, result in a matrix
                m <- expand.grid( c('E'), 
                                  c('C','X'), 
                                  c('E','X'), 
                                  c('T','X') )
                
                # then, optionally, apply to paste the columns together
                apply(m, 1, paste0, collapse='')[-1]
                
                [1] "EXET" "ECXT" "EXXT" "ECEX" "EXEX" "ECXX" "EXXX"
                

                【讨论】:

                • 如果m 的构建是通过字符串而不是手动输入完成的,那将是一个完整的答案。 (但大多数情况下,这将是穆迪的第二个选择)
                • 穆迪作为单线解决方案的第二个选项确实非常出色。但它非常简洁,包含很多内容。我认为这种方式也值得展示,因为它更清楚每一步发生的事情。问题很简单,不需要编码就可以将输入放入 expand.grid()
                • 我假设这个问题只是以 4 个字母为例(可能是某种生物序列),然后希望将其应用于大量数字,因此展示如何在 m 中构建各种向量在我看来更好
                • 我认为展示一个直观的解决方案是很有用的,即使它不是通用的。我已经更新了我的答案,使我的第二个解决方案更容易理解:)
                猜你喜欢
                • 2021-12-27
                • 1970-01-01
                • 2012-10-10
                • 2021-12-10
                • 2010-12-20
                • 2022-08-17
                • 2020-08-18
                • 2023-03-07
                • 1970-01-01
                相关资源
                最近更新 更多