I have a vector of zero and ones v <- c(0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0)
. What I want is a function func
do is to carry forward 1
until the next 1
is reached starting from the left, and then stop. If there is a new 1 after that, then the process should begin repeat. If a subsequent 1
is not found, 1
will be carried forward until the end of the vector. So here:
v
is c(0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0)
func(v)
should give c(0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0)
Any help appreciated.
CodePudding user response:
This sounds like a run-length encoding problem:
func <- function(z) {
r <- rle(z)
ind <- which(r$values == 0)
r$values[ ind[seq_along(ind) %% 2 == !(r$values[1] == 0)] ] <- 1
inverse.rle(r)
}
func(vec)
# [1] 0 1 1 1 1 1 0 0 1 1 1 0
Walk-through:
First, a run-length encoding gives each unique value and the consecutive repeats for it, so
rle(v) # Run Length Encoding # lengths: int [1:9] 1 1 3 1 2 1 1 1 1 # values : num [1:9] 0 1 0 1 0 1 0 1 0
Interpretation: the first value is a
0
and it has 1 instance; the second is a1
(with 1); the third is a0
with 3 instances; .etc.From there, knowing that we can call
inverse.rle(r)
to regenerate the originalv
, we need to replace alternating0
s with1
: to know which we need to replace depends on the first value: if the first is a0
, then we need to replace the even instances of0
withinr$values
.seq_along(ind) %% 2
determines if a specificind
(indices of0
) is an even or an odd index;!(r$values[1] == 0)
gives us the correct odd/even component we want.Once we know which (evens or odds) need to shift from
0
to1
, we just reassign it, and seer # Run Length Encoding # lengths: int [1:9] 1 1 3 1 2 1 1 1 1 # values : num [1:9] 0 1 1 1 0 1 1 1 0
It may be clear from here that if we invert it back into a vector, we should get one
0
, one1
, three1
, one1
, two0
, etc, exactly what we want.
CodePudding user response:
def fill_holes_with_ones(arr):
found, idx = False, -1
for i, el in enumerate(arr):
if found and el == 1:
for j in range(idx, i):
arr[j] = 1
found = False
elif el == 1:
idx = i
found = True
return arr
The above code is in python but since this is primarily an algorithmic question and python is easy enough to read I think you can translate it with relative ease.
This algorithm runs in O(n)
time complexity (maximum 2 pass) and O(1)
space.
The idea is to have a flag to indicate if you have already found an opening 1
or not. Also have an index that keeps track of the last found opening 1
. If you have already found an opening 1
and see another one, set all elements between the index that you previously captured and the current index to 1
and set the flag to False to invalidate the previous opening. Otherwise, if you find a 1
it only means that you have found an opening 1
, set the flag to true and capture the current index for potential later use.
CodePudding user response:
A solution based on converting the numeric vector to string and back to numeric vector:
library(tidyverse)
v <- c(0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0)
z <- paste(v,collapse = "")
z %>%
str_extract_all("(10 1)") %>%
unlist() %>%
reduce(
function(x,y) str_replace(
x,
paste0("(?<!1)",y),
paste(rep("1", nchar(y)), collapse = "")),
.init = z) %>%
str_split("") %>%
unlist() %>%
as.numeric()
#> [1] 0 1 1 1 1 1 0 1 1 1 0