Создать все комбинации замены букв в строке - PullRequest
0 голосов
/ 06 сентября 2018

У меня есть строка «ECET», и я хотел бы создать все возможные строки, в которых я заменяю одну или несколько букв (все, кроме первой) на «X».

Так что в этом случае мой результат будет:

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

Есть идеи, как подойти к вопросу?

Это не только создание возможных комбинаций / перестановок "X", но и способ их объединения с существующей строкой.

Ответы [ 7 ]

0 голосов
/ 06 сентября 2018

Вид ради добавления еще одной опции с использованием двоичной логики:

Предполагая, что ваша строка всегда имеет длину 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 логических значения, так как мы никогда не будем запускать последний бит (8 для 4 символов), он никогда не будет истинным:

> 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 с collapse как ничто, чтобы воссоздать одну строку и получить символьный вектор.

0 голосов
/ 07 сентября 2018

Еще одно простое решение

# 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"
0 голосов
/ 06 сентября 2018

Вот рекурсивное решение:

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<-:

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
0 голосов
/ 06 сентября 2018

Другая версия с 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)))
0 голосов
/ 06 сентября 2018

Векторизованный метод с логическим индексированием:

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"  
0 голосов
/ 06 сентября 2018

Вот базовое решение 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))
0 голосов
/ 06 сентября 2018

Использование аргумента FUN combn:

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"
...