How do you check if one array is a subsequence of another? - algorithm

I'm looking to explore different algorithms, both recursive and dynamic programming, that checks if one arrayA is a subsequence of arrayB. For example,
arrayA = [1, 2, 3]
arrayB = [5, 6, 1, 7, 2, 9, 3]
thus, arrayA is indeed a subsequence of arrayB.
I've tried a few different searches, but all I can seem to find is algorithms to compute the longest increasing subsequence.

Since you must match all elements of arrayA to some elements of arrayB, you never need to backtrack. In other words, if there are two candidates in arrayB to match an element of arrayA, you can pick the earliest one, and never retract the choice.
Therefore, you do not need DP, because a straightforward linear greedy strategy will work:
bool isSubsequence(int[] arrayA, int[] arrayB) {
int startIndexB = 0;
foreach (int n in arrayA) {
int next = indexOf(arrayB, startIndexB , n);
if (next == NOT_FOUND) {
return false;
}
startIndexB = next+1;
}
return true;
}

As dasblinkenlight has correctly said(and i could not have phrased it better than his answer!!) a greedy approach works absolutely fine. You could use the following pseudocode (with just a little more explanation but totally similar to what dasblinkenlight has written)which is similar to the merging of two sorted arrays.
A = {..}
B = {..}
j = 0, k = 0
/*j and k are variables we use to traverse the arrays A and B respectively*/
for(j=0;j<A.size();){
/*We know that not all elements of A are present in B as we
have reached end of B and not all elements of A have been covered*/
if(k==B.size() && j<A.size()){
return false;
}
/*we increment the counters j and k both because we have found a match*/
else if(A[j]==B[k]){
j++,k++;
}
/*we increment k in the hope that next element may prove to be an element match*/
else if(A[j]!=B[k]){
k++;
}
}
return true; /*cause if we have reached this point of the code
we know that all elements of A are in B*/
Time Complexity is O(|A|+|B|) in the worst case, where |A| & |B| are the number of elements present in Arrays A and B respectively. Thus you get a linear complexity.

As #sergey mentioned earlier, there is no need to do backtracking in this case.
Here just another Python version to the problem: [Time complexity: O(n) - worst]
>>> A = [1, 2, 3]
>>> B = [5, 6, 1, 7, 8, 2, 4, 3]
>>> def is_subsequence(A, B):
it = iter(B)
return all(x in it for x in A)
>>> is_subsequence(A, B)
True
>>> is_subsequence([1, 3, 4], B)
False
>>>

Here is an example in Ruby:
def sub_seq?(a_, b_)
arr_a = [a_,b_].max_by(&:length);
arr_b = [a_,b_].min_by(&:length);
arr_a.select.with_index do |a, index|
arr_a.index(a) &&
arr_b.index(a) &&
arr_b.index(a) <= arr_a.index(a)
end == arr_b
end
arrayA = [1, 2, 3]
arrayB = [5, 6, 1, 7, 2, 9, 3]
puts sub_seq?(arrayA, arrayB).inspect #=> true

Here is an example in GOLANG...
func subsequence(first, second []int) bool {
k := 0
for i := 0; i < len(first); i++ {
j := k
for ; j < len(second); j++ {
if first[i] == second[j] {
k = j + 1
break
}
}
if j == len(second) {
return false
}
}
return true
}
func main(){
fmt.Println(subsequence([]int{1, 2, 3}, []int{5, 1, 3, 2, 4}))
}

Related

Dafny: verify test case of array sorting with duplicates

Given the usual specification of array sorting in Dafny,
method sort(a: array<T>)
modifies a
ensures isSorted(a[..])
ensures multiset(a[..]) == multiset(old(a[..]))
predicate isSorted(s: seq<T>) {
forall i, j :: 0 <= i < j < |s| ==> s[i] <= s[j]
}
Dafny is able to check a "test case" without duplicates (with a small help)
method testSortWithoutDups() {
var a := new T[] [9, 4, 6, 3, 8];
assert a[..] == [9, 4, 6, 3, 8]; // small prover helper!
sort(a);
assert a[..] == [3, 4, 6, 8, 9];
}
But is not able to check a "test case" with duplicates, so I tried using a lemma as follows:
method testSortWithDups() {
var a := new T[] [9, 3, 6, 9];
assert a[..] == [9, 3, 6, 9]; // small prover helper!
sort(a);
SortingProp(a[..], [3, 6, 9, 9]);
assert a[..] == [3, 6, 9, 9];
}
// State and prove by induction the property that, if two sequences are sorted and have
// the same elements, then they must be identical (so sorting has a unique solution).
// Most of the proof steps are filled in by Dafny.
lemma SortingProp(a: seq<T>, b: seq<T>)
requires isSorted(a) && isSorted(b) && multiset(a) == multiset(b)
ensures a == b
{
seqProps(a);
seqProps(b);
if |a| > 0 {
SortingProp(a[1..], b[1..]);
}
}
// Two usefull properties about sequences and their multisets (proved by Dafny alone):
lemma seqProps(a: seq<T>)
ensures |a| > 0 ==> a == [a[0]] + a[1..]
ensures forall i :: 0 <= i < |a| ==> a[i] in multiset(a)
{}
Is there a better solution?
Yeah, it's too much to hope for that the verifier would prove this entirely automatically. Your decomposition into lemmas and the particular properties you're stating are perfect! Well done!

Shuffle an int array such that array elements in even indices are smaller than array elements in odd indices

I need to have all the elements in the even indices arr[0],arr[2],arr[4] etc be smaller than the elements with odd indices arr[1],arr[3],arr[5], etc
My approach was to find the MEDIAN and then write out all elements smaller than the median in odd indices and all elements larger than the median in even places.
Question: is there a way to do the array shuffling IN PLACE after finding the median ?
import random
def quickselect(items, item_index):
def select(lst, l, r, index):
# base case
if r == l:
return lst[l]
# choose random pivot
pivot_index = random.randint(l, r)
# move pivot to beginning of list
lst[l], lst[pivot_index] = lst[pivot_index], lst[l]
# partition
i = l
for j in range(l+1, r+1):
if lst[j] < lst[l]:
i += 1
lst[i], lst[j] = lst[j], lst[i]
# move pivot to correct location
lst[i], lst[l] = lst[l], lst[i]
# recursively partition one side only
if index == i:
return lst[i]
elif index < i:
return select(lst, l, i-1, index)
else:
return select(lst, i+1, r, index)
if items is None or len(items) < 1:
return None
if item_index < 0 or item_index > len(items) - 1:
raise IndexError()
return select(items, 0, len(items) - 1, item_index)
def shuffleArray(array, median):
newArray = [0] * len(array)
i = 0
for x in range(0,len(array),2):
newArray[x] = array[i]
i+=1
for y in range(1,len(array),2):
newArray[y] = array[i]
i+=1
return newArray
So here's my interpretation of the question.
Shuffle an array so that all data in even indices are smaller than all data in odd indices.
Eg
[1, 3, 2, 4] would be valid, but [1, 2, 3, 4] wouldn't be.
This stops us just being able to sort the array.
Sort the array, smallest to largest.
Split the array at its mid point (rounding the mid point down).
Shuffle the two arrays together. Such that given array [1, 2, 3] and array [4, 5, 6] it becomes [1, 4, 2, 5, 3, 6].
To elaborate on 3, here's some example code... (using javascript)
let a = [ 1, 2, 3 ];
let b = [ 4, 5, 6 ];
let c = [ ] // this will be the sorted array
for (let i = 0; i < a.length + b.length; i++ ) {
if(i % 2 == 0) c.push( a[Math.floor( i/2 )]);
else c.push( b[Math.floor( i/2 )]);
}
This produces the array [1, 4, 2, 5, 3, 6], which i believe fufils the requirement.

backtracking n staircases at most k steps in a single jump

You need to climb a staircase that has n steps, and you decide to get some extra exercise by jumping up the steps. You can cover at most k steps in a single jump. Return all the possible sequences of jumps that you could take to climb the staircase, sorted.
My implementation is obviously giving me the wrong answer.
def climbingStaircase(n, k):
final_res=[]
final_res.append(CSR(n,k,[]))
return final_res
def CSR(n,k,res):
if n == 0:
return res
else:
for i in range(1,k+1):
if n-i>=0:
res.append(i)
n=n-i
res=CSR(n,i,res)
return res
For n = 4 and k = 2, the output should be
[[1, 1, 1, 1],
[1, 1, 2],
[1, 2, 1],
[2, 1, 1],
[2, 2]]
Actual output:
[[1,1,1,1,2,1]]
Can someone point out which part I'm missing?
One huge problem is in the code below: you deduct the quantity of steps for each possibility within the step range.
n=n-i
res=CSR(n,i,res)
When you're done exploring what you can do with a 1-step jump, you need to backtrack and try from the same starting point (this instance's original value of n) with a 2-step jump. Change the code to:
res = CSR(n-i, i, res)
This keeps the n value intact as you go through the loop.
In addition, you can't limit future jumps to the max of what you just took. Change that second parameter, too:
res = CSR(n-i, k, res)
That should get you moving. Also try this lovely debug blog for help. At least insert one or two tracing statements, such as
print n, k, res
at the top of your routine.
CAVEAT
This is not all of your trouble. The largest remaining problem is that CSR returns only one solution: every step you take is appended to the same list. You need a way to gather the completed solutions as separate lists; the append in climbingStaircase is executed only once, after CSR is entirely finished.
You need to recognize a completed solution at n==0.
DEBUGGING HELP
Here is a version of your program with the recursion parameters fixed, and debugging traces inserted.
indent = ""
def climbingStaircase(n, k):
final_res = []
final_res.append(CSR(n, k, []))
return final_res
def CSR(n, k, res):
global indent
indent += " "
print indent, n, k, res
if n == 0:
print "SOLUTION", res
else:
for i in range(1, k+1):
if n-i >= 0:
CSR(n-i, k, res + [i])
indent = indent[:-2]
print climbingStaircase(4, 2)
Note the use of "indent" to help visualize your recursion and backtracking. The critical part here is that, instead of updating res globally, I've left it as a local variable. I've also removed the return value for now, simply dumping to output the solutions as they're found. You can see how it works:
4 2 []
3 2 [1]
2 2 [1, 1]
1 2 [1, 1, 1]
0 2 [1, 1, 1, 1]
SOLUTION [1, 1, 1, 1]
0 2 [1, 1, 2]
SOLUTION [1, 1, 2]
1 2 [1, 2]
0 2 [1, 2, 1]
SOLUTION [1, 2, 1]
2 2 [2]
1 2 [2, 1]
0 2 [2, 1, 1]
SOLUTION [2, 1, 1]
0 2 [2, 2]
SOLUTION [2, 2]
[None]
With this stuff in place, I'm hopeful you can trace your logic and figure out how to capture the sequence of solutions at a level of your choosing.
Successfully implemented Prune's answer.
def climbingStaircase(n, k):
res=[]
CSR(n,k,[],res)
return res
def CSR(n,k,str_, res):
if n == 0:
res.append(str_)
else:
for i in range(1,k+1):
if n-i>=0:
CSR(n-i,k,str_+[i],res)
A quick Java version of this solution:
int[][] climbingStaircase(int n, int k) {
List<ArrayList<Integer>> list = new ArrayList<>();
climb(n, k, new ArrayList<Integer>(), list);
// convert to int[][]
int[][] result = new int[list.size()][];
for (int i=0; i<list.size(); i++) {
List<Integer> l = list.get(i);
int [] arr = new int[l.size()];
for (int j=0; j<l.size(); j++)
arr[j] = l.get(j);
result[i] = arr;
}
return result;
}
void climb(int n, int k, ArrayList<Integer> prev, List<ArrayList<Integer>> list) {
if (n==0) { // no more stairs, done climbing
list.add(prev);
} else {
for (int i=1; i<=k; i++) { // climb remaining stairs in intervals from 1 to k steps
if (i <= n) { // no need to test intervals larger than remaining # of stairs
ArrayList<Integer> branch = new ArrayList<>(prev);
branch.add(i);
climb(n-i, k, branch, list);
}
}
}
}
In Swift 5.5
func solution(n: Int, k: Int) -> [[Int]] {
var res_final = [[Int]]()
SRC(n: n, k: k, res: [], &res_final)
return res_final
}
var indent: String = ""
func SRC(n: Int, k: Int, res: [Int], _ res_final: inout [[Int]]) {
indent += " "
print(indent, n, k, res)
if n == .zero {
res_final.append(res)
print("Solution", res)
} else {
for i in 1...k {
if n-i >= .zero {
SRC(n: n-i, k: k, res: res + [i], &res_final)
}
}
}
indent = " "
}
solution(n: 4, k: 2)

Fast way of getting r-long combinations of set A that have at least one element from set B, which is a subset of A

For example, if A={0,1,2,3,4}, r=3 and B={1,4}, the result would be:
[0, 1, 2]
[0, 1, 3]
[0, 1, 4]
[0, 2, 4]
[0, 3, 4]
[1, 2, 3]
[1, 2, 4]
[1, 3, 4]
[2, 3, 4]
That's all the r-long combinations of A, excluding [0, 2, 3], because that one doesn't contain either 1 or 4.
The solution that I currently have is the following, using the fastest algorithm for getting normal combinations I know of, and just doing a simple check to see if combinations generated also contain an element of B (java):
int[] A = new int[]{0,1,2,3,4};
int[] B = new int[]{1,4};
int n = A.length;
int r = 3;
int[] picks = new int[r]; //Holds indexes of elements in A
for (int i = 0; i < picks.length; i++)
picks[i] = i;
int lastindex = picks.length - 1;
outer:
while (true) {
int at = lastindex;
while (true) {
picks[at] += 1;
if (picks[at] < n) {
int displacement = picks[at] - at; // at + displacement = picks[at], at + displacement + 1 = picks[at] + 1 ,...
// Make all picks elements after at y = picks[at] + x, so picks={0, 2, 4, 6, 18, 30} & at=3 --> picks={0, 2, 4, 5, 6, 7}
// (Note that this example will never take place in reality, because the 18 or the 30 would be increased instead, depending on what n is)
// Do the last one first, because that one is going to be the biggest,
picks[lastindex] = lastindex + displacement;
if (picks[lastindex] < n) { // and check if it doesn't overflow
for (int i = at + 1; i < lastindex; i++)
picks[i] = i + displacement;
int[] combination = new int[r];
for (int i = 0; i < r; i++)
combination[i] = A[picks[i]];
System.out.print(Arrays.toString(combination));
//^With this, all r-long combinations of A get printed
//Straightforward, bruteforce-ish way of checking if int[] combination
//contains any element from B
presence:
for (int p : combination) {
for (int b : B) {
if (p==b) {
System.out.print(" <-- Also contains an element from B");
break presence;
}
}
}
System.out.println();
break;
}
}
at--;
if (at < 0) {
//Moving this check to the start of the while loop will make this natively support pick 0 cases (5C0 for example),
//but reduce performance by I believe quite a bit. Probably better to special-case those (I haven't
// done that in this test tho)
break outer;
}
}
}
output:
[0, 1, 3] <-- Also contains an element from B
[0, 1, 4] <-- Also contains an element from B
[0, 2, 3]
[0, 2, 4] <-- Also contains an element from B
[0, 3, 4] <-- Also contains an element from B
[1, 2, 3] <-- Also contains an element from B
[1, 2, 4] <-- Also contains an element from B
[1, 3, 4] <-- Also contains an element from B
[2, 3, 4] <-- Also contains an element from B
As written in the comments, I believe this method to be very rudimentary. Can anyone think of a faster way to do this?
Assuming you have a int[][] FindCombinations(int[] set, int length) function that returns a list of all the length-long combinations of set, do the following (pseudo-code):
for i=1 to B.length
{
int bi = B[i];
A = A - bi; // remove bi from A
foreach C in FindCombinations(A, r-1)
{
output C+bi // output the union of C and {bi}
}
}
This way all combinations contain at least one element from B (and may also contain elements of B that have not yet been used) without much extra work. All other combinations are eliminated at no cost (the don't have to be found at all) and also the test that a combination contains an element from B for each combination is also eliminated.
Whether this algorithm is faster, greatly depends on how efficently you can add/remove elements from a set and the percentage of included vs excluded combinations (i.e. if you only end up excluding 1% of the total combinations it is probably not worth it)
Note that when getting the combinations to union with {b[i]} these may also contain an element B[j] where j>i. When you get to the point that you get the combinations to union with B[j] none of them will contain B[i], so all combinations are unique.

Rotating elements i to j in an array

Given an array:
a = [1, 2, 3, 4, 5, 6]
I want to rotate elements i through j in some direction n times. So, for example:
i = 2
j = 3
n = 1
Rotating a will produce:
new_a = [1, 2, 4, 3, 5, 6]
This is what I have:
def rotate_sub(a, i, j, n)
return a[0...i] + a[i..j].rotate(n) + a[j+1..-1]
end
Is there a better way to do this? Since there is no bound-checks, i or j could very well be outside the bounds of the array.
If you are willing to mutate the original array you could do something like:
a[i..j] = a[i..j].rotate n
But I like the functional solution you already have.
I don't think there's a magical way, so perhaps the simplest is the best:
def rotate_sub(a, i, j, n)
a[0...i] + a[i..j].rotate(n) + a[j+1..-1] if i < j && j < a.size
end

Resources