I am trying the "CountDistinctSlices" codility question. I tried my best scored 30% so tried to look up on someone who did it for insights. and basically what I don't get in the answer is the use of the initialized seen array(and M for that matter) and how its being used can someone who get it kindly walk me through this code.
THis is the Answer I found without explanation
function solution(M, A) {
// write your code in JavaScript (Node.js 8.9.4)
let sum = 0;
let front = 0;
let back = 0;
const seen = new Array(M+1).fill(false);
while (front < A.length && back < A.length){
while (front < A.length && seen[A[front]] !== true){
sum += (front-back+1);
seen[A[front]] = true;
front += 1;
}
while (A[back] !== A[front]){
seen[A[back]] = false;
back += 1;
}
seen[A[back]] = false;
back += 1;
}
return Math.min(sum, 1000000000);
}
This is the full question
An integer M and a non-empty array A consisting of N non-negative
integers are given. All integers in array A are less than or equal to
M.
A pair of integers (P, Q), such that 0 ≤ P ≤ Q < N, is called a slice
of array A. The slice consists of the elements A[P], A[P + 1], ...,
A[Q]. A distinct slice is a slice consisting of only unique numbers.
That is, no individual number occurs more than once in the slice.
For example, consider integer M = 6 and array A such that:
A[0] = 3
A[1] = 4
A[2] = 5
A[3] = 5
A[4] = 2
There are exactly nine distinct slices: (0, 0), (0, 1), (0, 2), (1,
1), (1, 2), (2, 2), (3, 3), (3, 4) and (4, 4).
The goal is to calculate the number of distinct slices.
Write a function:
function solution(M, A);
that, given an integer M and a non-empty array A consisting of N
integers, returns the number of distinct slices.
If the number of distinct slices is greater than 1,000,000,000, the
function should return 1,000,000,000.
For example, given integer M = 6 and array A such that:
A[0] = 3
A[1] = 4
A[2] = 5
A[3] = 5
A[4] = 2
the function should return 9, as explained above.
Write an efficient algorithm for the following assumptions:
N is an integer within the range [1..100,000];
M is an integer within the range [0..100,000];
each element of array A is an integer within the range [0..M].
Lets go through the algorithm first:
You first start from the beginning and traverse until you find a duplicate. Now you have a range = [ back - front ]
The code called this range [back, front] where "back" is beginning and "front" is your moving pointer.
How many distinct slices are there in this range? There are slices of size 1, 2, .... front - back + 1, so it is sum = 1 + 2 + 3 + ... [front - back + 1]
Now that you encountered a duplicate what you should do ? To understand lets take the example in the question : [3,4,5,5,2]. Now front reached 5. Now we should bring the back pointer to 5 but also at the same time remove the elements 3, 4, 5 from the set because those may be present after the current front. So back comes to 5 which is currently pointed by front.
Lets take another example [1,2,1] , for this front will reach 1 at the index 2 because that is the first duplicate found. Now where should back come to? It should come to 2 because that will be the position where set won't have any duplicates when you delete the elements in the set while you move the back pointer.
Do this until the front reaches the end of the array.
Your question about seen:
How do you find a duplicate in an array? You could use either a Set or you could use a boolean array. I think the only purpose of M in this question is to specify the maximum value that an element in the array can have. So the code used a boolean array of size M.
If you use the boolean array, once you find an element of value say v you can just say boolean_arr[v] = true that means it was "seen".
Or you could use a set and create a new one when needed without having to clear your whole boolean array everytime you find a duplicate - by letting the JavaScript to handle the garbage collection - something like below ( not fully tested ):
function solution(M, A) {
let sum = 0;
let front = 0;
let back = 0;
let set = new Set();
while (front < A.length) {
while (front < A.length && !set.has(A[front])){
sum += (front-back+1);
set.add(A[front]);
front += 1;
}
while (A[back] != A[front]) {
set.delete(A[back]);
back += 1;
}
set.delete(A[back]);
back += 1;
}
return Math.min(sum, 1000000000);
}
Related
UPDATE AT THE BOTTOM OF THE POST!
This is the first part of a program that will eventually make SVG star polygons.
Only star polygons that can be made in one go, I.e. like a Pentagram, NOT like a Hexagram that can only be made with at least 2 shapes I.e. 2 inverted and overlapping triangles.
I'm almost finished with this. Everything seems to be working well and I'm getting the out put I want except there's an empty array item in every other array produced for some reason.
In this part
I'm printing to the console (for testing) an object with items named with numbers that represent the number of points / sides of a regular polygon. Within each item is an array with the lower half of all the non factoring numbers of that item (number) I.e. an array of numbers that when the number (item name) is divided by them, it will return a fraction.
E.g. Object
starPolygons{'5': 2, '7': 3, 2, '8': 3, '9': 4, 2, …}
(Representation of: Pentagon: Heptagon: Octagon: Nonagon: …)
What "qty_test" functions does
The points variable (= 8 for example) is sent to a function (qty_test) which is used to find the number of divisors I need to test and inside it is divided by 2. If the result is a fraction, it will return the result rounded back, and if its even, it will return the result - 1.
//8 has more divisors to cycle through -so its a better example!
//In the real program, it will be 5.
let points = 8;
let starPolygons = {};
E.g. qty_test
point (=8) ()=> n /= 2 (=4)
if n - n(rounded-back) = 0?
true returns (n -1) and false returns n(rounded-back)
function qty_test(n) {
n /= 2;
let divisorQTY = n - Math.floor(n) !== 0;
return divisorQTY ? Math.floor(n) : (n - 1);
};
What divisor_test functions does
This function uses the number returned from the previous function (n) to set a for loop that tests every cycle if n is not a factor of the value of the points variable. I.e. points divided by n returns a fraction and if it returns true, meaning a fraction was produced n`s value is indexed in an array and decreaces by 1 (--n).
E.g. divisor_test
n(=8) / 2 = 4 << 2 will be ignored.
n(=8) / 3 = 2.666... << 3 will be collected
function divisor_test(n) {
let nonFactors = [];
n = qty_test(n);
for (let index = 0; index <= n; index++) {
let quotient = (points / n) - Math.floor(points / n) !== 0;
quotient ? nonFactors[index] = n-- : --n;
};
return nonFactors;
};
The program cycles through 5 - 360 and indexes an object with numbers (5-360) and array list of their lower half non factor numbers.
while (points <= 360) {
nonFactors = divisor_test(points);
let testArray = nonFactors.length;
if (testArray) starPolygons[String(points)] = nonFactors;
points++;
};
console.log(starPolygons);
AS YOU CAN SEE IN THIS IMAGE. THE PATTERN OF EMPTY ARRAY INDEXES / ITEMS
UPDATE: I NOTICED THE PATTERN OF ARRAYS WITH THE EMPTY SLOTS HAS AN INTERESTING QUALITY TO IT. This is so cool and so confusing as to what causes this...
During the Iteration you are forcing the array to add an empty item.
So, the empty object appears when the array has only two items in it and you are trying to nonFactors[3] = n--;
and thats what causes the issue.
So a more simplified version of the for loop with the solution will be
for (let index = 0; index <= n; index++) {
let quotient = (points / n) - Math.floor(points / n) !== 0;
if(quotient){
if(index > nonFactors.length){
nonFactors[index - 1] = n--
}else{
nonFactors[index] = n--
}
}else{
n = n -1;
}
};
I solved my problem
There's an answer here that already explains what causes the problem and also gives a working solution by Nadav Hury (upvote his answer. I bet his answer generally works better in more situations)! So I will just post my own version of a solution.
for (let index = 0; n >= 2; index++) {
let quotient = (points / n) - Math.floor(points / n) !== 0;
if (quotient) nonFactors[index] = n--;
else --n, --index;
};
The culprit is that in the line with checking the quotient
quotient ? nonFactors[index] = n-- : --n;
you added setting the value only for the case when it is present that is equal to true but you didn't add to the case when it is false correct solution is:
quotient ? nonFactors[index] = n-- : nonFactors[index] = --n;.
For clarity sake, this is what I mean. I want to look for the two least numbers in an array(sorted) that will generate a particular number. The steps go thus:
Loop through the array and each time set a current value that other
numbers will be deducted from.
Keep doing that till you find the numbers that match the problem and return them.
Example. I need two numbers that when subtracted from the array will give a result of 2.
let givenArray = [1, 4, 8, 10];
The subtraction should go thus: 4 - 1 = 3(doesn't match); //continue
8 - 4 = 1(doesn't match);// continue
8 - 1 = 7(doesn't match); //continue
10 - 8 = 2(match found); //stop and return 8, 10.
NOTE: This same array may contain a 6 and 8 or 8 and 10 that will both yield 2 but 6 and 8 should be returned instead. The way the array is generated isn't of much importance.
P.S: I eventually solved it yesterday but I won't mind other ideas on how to go about it.
This solution takes the advantage of a hash table and uses a single loop approach for getting the two values out of an array to balance two values.
First, take the absolute delta of the two values of arrayA and take this for getting the values out of the greater array.
Then reduce the greater array arrayB by checking if the needed value exist and if the sum is smaller then a previously found set.
The argument for checking is build out of the absolute delta of delta and v, the actual value of the array or by taking the sum of delta and v.
The last point, and to make this all working, the actual value v is included into the hash table, for a later look up.
The result is either an array of two values, which balance the other two values or undefined, if no values are found.
var arrayA = [3, 5],
arrayB = [2, 9, 5, 4],
delta = Math.abs(arrayA[0] - arrayA[1]),
values = {},
result = arrayB.reduce((r, v) => {
function check(w) {
if (!values[w] || r && r[0] + r[1] < v + w) return;
r = [w, v];
}
check(Math.abs(delta - v));
check(delta + v);
values[v] = true;
return r;
}, undefined);
console.log(result);
I'm not sure I understood correctly, but perhaps this is what you need:
let result = arrayA[1] - arrayA[0];
let i, j;
for (i = arrayB.length - 1; i >= 1; i--) { // Set the first value
for (j = arrayB.length - 1; j >= 1; j--) { // Set the second value
if (i !== j) {
if (
arrayB[i] - arrayB[j] === result // Check substraction
|| arrayB[i] + arrayB[j] === result // Check addition
) return [arrayB[i], arrayB[j]];
}
}
}
I have tried to calculate binary of a number provided in an array using naive for-loop method.
Than I find a solution which works but I am not able to understand how it is working.
I need help to understand this.
const binaryArrayToNumber = arr => {
return arr.reduce((a,b)=>(a<<1|b),0);
};
console.log(binaryArrayToNumber([1,1,1,1]))
Explaining your code:
The << left logical shift) shifts the number to the left. If the number n is 00010110 then:
n << 1 will be 00101100.
n << 2 will be 01011000.
etc
The operator | (betwise or) performs an iclusive logical OR on the bits at the same positions. If the numbers n and m were, respectively 00110100 and 10010101 then:
n = 00110100
m = 10010101
----------------
n | m = 10110101
following these rules: 0 | 0 = 0, 0 | 1 = 1, 1 | 0 = 1 and 1 | 1 = 1.
Now for the reduce part: reduce loop through an array, passing an accumulator (initially set by the user, the last parameter) and the current item of the array to a callback (the function passed as the first parameter) and set the value of the accumulator to the returned value of that callback.
So for each item in the array we shifts the accumulator to the left to make a place for the new bit and then add the bit to that place using the betwise or. Here is an example with explanation:
var arr = [1, 0, 1];
reduce will start with an accumulator equal to 0 (...,0); at end of reduce line) then pass it along with the current item (the first item) to the callback. The the callback will shift the accumulataror (a) to the left by 1:
First shift the accumulator:
a = 00000000;
a << 1 = 00000000;
And then return the betwise or result of the shifted a with the current item from the array b:
b = 00000001;
00000000 | b = 00000001;
Now the new accumulator is the result of the above code (00000001).
reduce then will pass the current accumulator along with the current item from the array (now the second one) to the callback again:
First:
a = 00000001;
a << 1 = 00000010;
and:
b = 00000000;
00000010 | b = 00000010;
reduce will do the same thing for the last item of the array:
First:
a = 00000010;
a << 1 = 00000100;
and:
b = 00000001;
00000100 | 00000001 = 00000101;
Since there is no more items in the array, reduce will return the accumulated value (the accumulator return by the last call to the callback i.e a) as the result.
If the syntax return arr.reduce((a,b)=>(a<<1|b),0); isn't clear for you, it's because you're not familliar with Arrow functions. The exact line could be written using regular functions like this:
return arr.reduce(function(a, b) { // a the accumulator, b the curent item from the array
return (a << 1) | b; // return something to store as the new accumulator
}, 0; // 0 is the initial value of the accumulator
Another way to do it:
without using any binary operation nor reduce:
var arr = [1, 0, 1, 1];
var number = parseInt(arr.join(''), 2);
console.log(number);
arr.join('') will return a string (a concatenation of all the items in the array "1011"). Then parseInt will parse that string as being a binary number.
The reduce function works as an accumulator. The (a,b) pair is actually (total, currentValue). For instance, if you want to calculate sum of 1, 2 and 3, you can use the following code for that:
var sum = [1, 2, 3].reduce(
function(total, currentNumber){ return total + currentNumber; }
, 0);
For each iteration, a value of the total variable is increased for currentNumber and after all elements of the array were iterated, the total is being assigned to the sum variable.
The second parameter of the anonymous function (in this case 0) is the initial value of the sum (before iterating the array elements).
So, the code above is same as this code:
var sum = 0;
for (var i = 1; i <=3; i++)
{
sum = sum + i;
}
I entered a coding test where one of the questions was this: given an array A of integers of any length, and then two numbers N and Z, say whether there are Z (distinct) numbers in A such as their sum is N.
So for example (in the format A N Z):
for [1,2,3] 5 2 the answer is YES because 2+3=5
for [1,2,3] 6 2 the answer is NO because there are no two numbers in A that can be added to make 6
My solution (below) first enumerates every (unordered) combination of Z numbers in A, then sums it, and then searches for N in the list of sums.
Although this solution works fine (passed all test cases, with no timeout), I was told the score was too low for me to continue in the test.
So the question is, what can be improved?
An obvious optimization would be to calculate the sum of each combination immediately, and then stop when a match with N is found; but since I didn't run into time issues I don't think this is the problem. What is the better, more elegant/efficient solution?
function main(a, n, z) {
var spacea = [], // array of unordered combinations of z integers from a
space = [], // array of unique sums of combinations from spacea
res=0; // result (1 or 0)
// produce combination
spacea = combo(a,z);
// put unique sums in space
spacea.forEach(function(arr) {
var s = arr.reduce(function(a,b) {
return a+b;
});
if (space.indexOf(s)<0) space.push(s);
});
// is n in space?
res = space.indexOf(n) === -1 ? "NO" :"YES";
return res;
}
// produces combinations (outputs array of arrays)
function combo(a, z) {
var i,
r = [],
head,
right;
if (z > a.length || z <= 0) {
// do nothing, r is already set to []
}
else if (a.length === z) {
r = [a];
}
else if (1 === z) {
// r = array of array of values from a
a.forEach(function(e) {
r.push([e]);
});
}
else { // by virtue of above tests, z>1 and z<a.length
for (i=0; i<a.length-z+1; i++) {
head = a.slice(i, i+1);
right = combo(a.slice(i+1), z-1);
right.forEach(function(e) {
r.push(head.concat(e));
});
}
}
return r;
}
This is a variation of the subset sum problem, which can be solved with Dynamic Programming for more efficient solution.
The main difference here, is you have an extra restriction - the number of elements that must be used. This extra restriction can be handled by adding another variable (dimension) - the number of already used elements.
The recursive formulas (which you will build the DP solution from) should be:
D(0,0,0) = true
D(i,k,x) = false if i < 0 or k < 0
D(i,k,x) = D(i-1, k, x) OR D(i-1, k-1, x - arr[i])
In the above, D(i,k,x) is true if and only if there is a solution that uses k exactly k numbers, from the first i elements, and sums to x.
Complexity of this solution is O(n*N*Z) where n - number of elements in the array, N - number of distinct elements you can use, Z - target sum.
I have to determine the mathematical formula to calculate a particular repeating position in a series of numbers. The list of numbers repeats ad infinitum and I need to find the number every n numbers in this list. So I want to find the *n*th item in a list of repeating y numbers.
For example, if my list has 7 digits (y=7) and I need every 5th item (n=5), how do I find that item?
The list would be like this (which I've grouped in fives for ease of viewing):
12345 67123 45671 23456 71234 56712 34567
I need to find in the first grouping number 5, then in the second grouping number 3, then 1 from the third group, then 6, then 4, then 2, then 7.
This needs to work for any number for y and n. I usually use a modulus for finding *n*th items, but only when the list keeps increasing in number and not resetting.
I'm trying to do this in Javascript or JQuery as it's a browser based problem, but I'm not very mathematical so I'm struggling to solve it.
Thanks!
Edit: I'm looking for a mathematical solution to this ideally but I'll explain a little more about the problem, but it may just add confusion. I have a list of items in a carousel arrangement. In my example there are 7 unique items (it could be any number), but the list in real terms is actually five times that size (nothing to do with the groups of 5 above) with four sets of duplicates that I create.
To give the illusion of scrolling to infinity, the list position is reset on the 'last' page (there are two pages in this example as items 1-7 span across the 5 item wide viewport). Those groups above represent pages as there are 5 items per page in my example. The duplicates provide the padding necessary to fill in any blank spaces that may occur when moving to the next page of items (page 2 for instance starts with 6 and 7 but then would be empty if it weren't for the duplicated 1,2 and 3). When the page goes past the last page (so if we try to go to page 3) then I reposition them further back in the list to page one, but offset so it looks like they are still going forwards forever.
This is why I can't use an array index and why it would be useful to have a mathematical solution. I realise there are carousels out there that do similar tasks to what I'm trying to achieve, but I have to use the one I've got!
Just loop every 5 characters, like so:
var data = "12345671234567123456712345671234567";
var results = [];
for(var i = 4; i < data.length; i += 5){
results.push(data[i]);
}
//results = [5, 3, 1, 6, 4, 2, 7]
If you want to use a variable x = 5; then your for loop would look like this:
for(var i = x - 1; i < data.length; i += x){...
There is no need to know y
If your input sequence doesn't terminate, then outputting every nth item will eventually produce its own repeating sequence. The period (length) of this repetition will be the lowest common multiple of the period of the input sequence (y) and the step size used for outputting its items (x).
If you want to output only the first repetition, then something like this should do the trick (untested):
var sequence = "1234567";
var x = 5;
var y = sequence.length;
var count = lcm(x, y);
var offset = 4;
var output = [];
for (var i = 0; i < count; i += x)
{
j = (offset + i) % y;
output.push(sequence[j]);
}
You should be able to find an algorithm for computing the LCM of two integers fairly easily.
A purely mathematical definition? Err..
T(n) = T(n-1) + K For all n > 0.
T(1) = K // If user wants the first element in the series, you return the Kth element.
T(0) = 0 // If the user want's a non-existent element, they get 0.
Where K denotes the interval.
n denotes the desired term.
T() denotes the function that generates the list.
Lets assume we want every Kth element.
T(1) = T(0) + K = K
T(2) = T(1) + K = 2K
T(3) = T(2) + K = 3K
T(n) = nk. // This looks like a promising equation. Let's prove it:
So n is any n > 1. The next step in the equation is n+1, so we need to prove that
T(n + 1) = k(n + 1).
So let's have a go.
T(n+1) = T(N+1-1) + K.
T(n+1) = T(n) + K
Assume that T(n) = nk.
T(n+1) = nk + k
T(n+1) = k(n + 1).
And there is your proof, by induction, that T(n) = nk.
That is about as mathematical as you're gonna get on SO.
Nice simple recurrence relation that describes it quite well there.
After your edit I make another solution;)
var n = 5, y = 7;
for (var i = 1; i<=y; i++) {
var offset = ( i*y - (i-1)*n ) % y;
var result = 0;
if (offset === n) {
result = y;
} else {
result = (n - offset) > 0 ? n - offset : offset;
}
console.log(result);
}
[5, 3, 1, 6, 4, 2, 7] in output.
JSFIDDLE: http://jsfiddle.net/mcrLQ/4/
function get(x, A, B) {
var r = (x * A) % B;
return r ? r : B;
}
var A = 5;
var B = 7;
var C = [];
for (var x = 1; x <= B; ++x) {
C.push(get(x, A, B));
}
console.log(C);
Result: [5, 3, 1, 6, 4, 2, 7]
http://jsfiddle.net/xRFTD/
var data = "12345 67123 45671 23456 71234 56712 34567";
var x = 5;
var y = 7;
var results = [];
var i = x - 1; // enumeration in string starts from zero
while ( i <= data.length){
results.push(data[i]);
i = i + x + 1;// +1 for spaces ignoring
}