How to determine the time and space complexity of this algorithm? - javascript

As I am preparing for an upcoming interview I worked on the string permutations problem. -
Problem statement - Write a function for generating all permutations of an input string.
Here's the solution that I feel is pretty good.
function getPermutations(string) {
// base case
if (string.length <= 1) {
return new Set(string);
}
var allCharsExceptLast = string.slice(0, -1);
var lastChar = string[string.length - 1];
// recursive call: get all possible permutations for all chars except last
var permutationsOfAllCharsExceptLast = getPermutations(allCharsExceptLast);
// put the last char in all possible positions for each of the above permutations
var permutations = new Set();
permutationsOfAllCharsExceptLast.forEach(function(permutationOfAllCharsExceptLast) {
for (var position = 0; position <= allCharsExceptLast.length; position++) {
var permutation = permutationOfAllCharsExceptLast.slice(0, position) + lastChar + permutationOfAllCharsExceptLast.slice(position);
permutations.add(permutation);
}
});
return permutations;
}
Even though I understand the solution (took me a few tries and about a million console logs), recursion confuses the crap out of me. Would someone please break down time and space complexity for me?

Let's think about the process. Say our String is n characters long. First, we must pass over each character in the string (n operations), and then for each character, recursively generate a permutation of the other n-1 characters in the string, from which we will for each 2nd character, recursively generate a permuation of n-2 chars in the string, and so on... until there is only 1 char left. To calculate the total time complexity, we multiply all these terms together (n * (n-1) * (n-2) * ... * 1 = n!), getting a time complexity in Big-O notation of O(n!).
To think about why we multiply them, we can think of the much simpler problem as follows: How many different permutations are there of clothes to wear if we have 2 pairs of pants and 3 shirts. The answer is clearly six, and we get this by noting that for each shirt, we have two choices for pants, so we take the number of shirts and multiply by the number of pants.
We can translate this example to a easy string, say the word "cat". To get every permutation, your code first chooses a character (it does not matter what order you choose the characters in, so I will first choose the 'c'), and then find the permutations in the remaining string, in this case "at". It is trivial that the only two permuations of this are "at" and "ta", so we add the strings "atc" and "tac" to overall permutations. Next, we take out 'a', and the remaining String is "ct", from which the permutations are "ct" and "tc". Thus, we add "cta" and "tca" to our overall permutations. Finally, doing the same thing when we take out 't', we end up with "ca" and "ac" as our remaining permutations, so we add "cat" and "act" to our overall permuations, and we are done. Notice that in this case they are all unique, but if a letter had been duplicated, (such as in "wow"), then your algorithm will double count, which is okay, as this not really necessary to account for.
Anyway, hope this helps, please leave a comment if you have an additional question.

Related

Splitting an array into columns, where the total child elements is roughly equal

I'm trying to achieve some tricky functionality within Javascript for creating a directory page. The directory page works by having items assigned to a letter (which is the first letter of the item).
For example:
{
A: [Alfie, Amelia, Ava, Alex, Aaron],
B: [Ben, Bella, Blake, Bailey, Bradley]
...
}
Essentially, the output should be 4 sub-arrays. These sub-arrays should be an array of letters. However, the functionality should decide how many letters are in each array by the number of items that are assigned to that letter.
So, the first array could contain only 3 letters, as each letter has 5 items assigned (totalling). The second array could contain 7 letters, as the total of sum of their children is roughly 15.
For example:
[
[A (5), B (5), C (5)],
[E (4), F (1), G (1), H (2), I (3), J (2), K (1)]
...
]
It's worth nothing that it isn't important for each of the 4 arrays to contain EXACTLY the same amount of items because it's likely for this not to be possible.
I'm not sure how I'd begin to achieve this functionality. Any points in the right direction would be much appreciated
Here is a simple approach for the question asked.
First, it is easy given maximum height of any column to figure out how many columns you will need. Just attempt it.
Next, the maximum height must be in the range from the largest number of items assigned to a letter, to the sum of all items assigned to all letters.
And now we can do a binary search to find the smallest allowed height that has at most 4 columns. The key loop should look something like:
while (lower < upper) {
let mid = Math.ceil((lower + upper)/2);
if (divideIntoColumns(items, mid).length <= 4) {
lower = mid;
}
else {
upper = mid - 1;
}
}
As #trincot pointed out, titles also take space. The easiest way to take that into account is do make you divideIntoColumns function be the estimated height of the column in a convenient unit (eg pixels). Even if that multiplies the size of your numbers by a factor of 20, that's just 4-5 extra rounds of binary search, which will be fine.

Shifting Strings in Javascript

I'm very new to programming and have been a bit shy when it comes to asking for help....to be honest, I get intimidated by how fast others can figure something out, so for those of you that are in the category, I was hoping you could help me out with a homework question. I Don't know where to begin or write the pseudocode, but if you can guide me or give me a response with details of the why and how, I'd owe you a huge debt. Here's the problem:
we define the following operations on a string:
left shifts: a single circular rotation of the string in which the first character becomes the last
character and all other characters are shifted one index to the left. For example, bcdea becomes
cdeab after a left shift.
right shifts: same as above but in reverse, the last character becomes the first.
the following parameters:
s: string to shift
left shift: integer
right shift: integer
constraints:
1 <= s <= 10^5
0 <= leftshifts, rightshifts <= 10^9
function getShiftedString(s, leftShifts, rightShifts) {
}
function getShiftedString(s, leftShifts, rightShifts) {
s = leftShifting(s, leftShifts);
return rightShifting(s, rightShifts);
}
function leftShifting(s, leftShifts) {
return s.substring(leftShifts) + s.substring(0, leftShifts);
}
function rightShifting(s, rightShifts) {
let l = s.length - rightShifts;
return leftShifting(s, l);
}
try this
Try writing it out in psudo code, that'll help you plan your function.
Think about what you need this function to do, you need it to:
accept a string
shift it to the right x number of times,
shift it to the left y number of times
so maybe your psudo code would look a little like this
s = string, x = left, y = right
convert s to array
for (x times)
q = first element in array
remove first element from array
add q to end of array
for (y times)
q = last element in array
remove last element from array
add q to the beginning of the array
make s string again
return s
Then it's just a simple matter of converting that to code.
Keep in mind, that's just one solution, and there are far better ones out there. definitely do what Neil Lunn said though, and look up string shifting.

How is this O(1) space and not O(n) space. firstNotRepeatingCharacter Challenge solution

I am having trouble understanding how the following solution is O(1) space and not O(n) space. The coding challenge is as follows:
Write a solution that only iterates over the string once and uses O(1) additional memory, since this is what you would be asked to do during a real interview.
Given a string s, find and return the first instance of a non-repeating character in it. If there is no such character then return '_'.
The following is a solution that is O(1) space.
function firstNotRepeatingCharacters(s: string) : string {
const chars: string[] = s.split('');
let duplicates = {};
let answer = '_';
let indexAnswer = Number.MAX_SAFE_INTEGER;
chars.forEach((element, index) => {
if(!duplicates.hasOwnProperty(element)) {
duplicates[element] = {
count: 1,
index
}
} else {
duplicates[element].count++;
duplicates[element].index = index;
}
});
for(const key in duplicates) {
if(duplicates[key].count === 1 && duplicates[key].index < indexAnswer) {
answer = key;
indexAnswer = duplicates[key].index;
}
}
return answer;
}
console.log(firstNotRepeatingCharacter('abacabad'));
console.log(firstNotRepeatingCharacter('abacabaabacaba'));
I do not understand how the above solution is O(1) space. Since we are iterating through our array we are mapping each element to an object (duplicate). I would think this would be considered O(n), could somebody clarify how this is O(1) for me. Thanks.
The memory usage is proportion to the number of distinct characters in the string. The number of distinct characters has an upper limit of 52 (or some other finite value) and the potential memory usage does not increase as n increases once each of the distinct characters has been seen.
Thus, there exists an upper limit on the memory usage that is constant (does not depend on n), so the memory usage is O(1).
Indeed this is an 0(1) complexity, but only on space constraints. Since we have an upper limit. This limit could be UTF-16, it could be the amount of English letters.
This is a constraint given by the Developer. Saying that, it's only a 0(1) in space constraints if the code above ran with a finite set of combinations.
A String it's limited by implementation to a 64 bit character "array". So the store capacity generally of a "String" type it's 2147483647 (2ˆ31 - 1) characters. That's not really what 0(1) represents. So virtually that's an 0(N) in space constraints.
Now the situation here it's totally different for time complexity constraints. It should be in the optimal scenario a 0(N) + 0(N - E) + 0(N).
Explaining:
1. First 0(N) the first loop goes through all the elements
2. Second 0(N) is about the deletion. The code delete's element's from the array.
3. 0(N - E) the second forEach loops the final popped array, so we have a constant E.
And that's supposing that the data structure is an Array.
There's a lot to Digg here.
TL;DR
It's not a 0(1).
The algorithm has O(min(a,n)) space complexity (where a is number of letters used for text cooding e.g. for UTF8 a>1M). For worst case: string with uniqe characters (in this case n<=a) e.g. abcdefgh the duplicates object has the same number of keys as number letters of input string - and what is clear on this case, the size of used memory depends on n.
The O(1) is only for case when string contains one repeated letter e.g. aaaaaaa.
Bonus: Your code can be "compressed" in this way :)
function firstNotRepeatingCharacters(s, d={}, r="_") {
for(let i=0; i<s.length; i++) d[s[i]]=++d[s[i]]|0;
for(let i=s.length-1; i>=0; i--) if(!d[s[i]]) r=s[i];
return r;
}
console.log(firstNotRepeatingCharacters('abacabad'));
console.log(firstNotRepeatingCharacters('abacabaabacaba'));

How can I find the missing integer in a string of random non-repeating integers in a range

I'm having trouble even beginning to think of how to do this.
I need to find a missing number in a string of random numbers that don't have separators.
Here's an example: 14036587109. In this example, the missing number is 2, the range is 0-10 (inclusive).
How can I write a JavaScript/Node.JS program to solve this?
The part I can't figure out is how the program separates the numbers; In the example above, how would the program know that the number 10 (before the last number) isn't the numbers 1 and 0.
There are two things we know about the missing integer: the total number of digits in the input tells us the number of digits in the missing integer, and (as #samgak mentioned in a comment) counting the occurrences of each digit in the input tells us which digits the missing integer is made of. This may give us a quick path to the solution, if one of the permutations of those digits is missing from the input. If it doesn't, then:
Find the integers from highest to lowest number of digits; if the range is e.g. 0-999, then search the 3-digit integers first, then 2, then 1.
If an integer is only present at one location in the input, mark it as found, and remove it from the input.
Then, start again with the longest integers that haven't been found yet, and look at the ones that are present at two locations; try both options, and then check whether all other integers that rely on the digits we're using are also present; e.g. if 357 is present at two locations:
... 1235789 ... 2435768 ...
357 357
23 43
123 243
235 435
578 576
78 76
789 768
When trying the first location for the 357, check whether there is another possibility for 23, 123, 235, 578, 78, and 789. For the second location, check 43, 243, 435, 576, 76 and 768.
If these checks show that only one of the options is possible, mark the number as found and remove it from the input.
Go on to do this for shorter integers, and for integers that are present at 3, 4, ... locations. If, after doing this to a certain point, there is still no result, you may have to recursively try several options, which will quickly lead to a huge number of options. (With especially crafted large input, it is probably possible to thwart this method and make it unusably slow.) But the average complexity with random input may be decent.
Actually, when you find an integer that is only present in one location in the input, but it is a permutation of the missing digits, you should not remove it, because it could be the missing integer. So the algorithm might be: remove all integers you can unequivocally locate in the input, then try removing all possible missing integers one by one, and look for inconsistencies, i.e. other missing numbers that don't have the correct length or digits.
It's all a question of heuristics, of course. You try something simple, if that doesn't work you try something more complicated, if that doesn't work, you try something even more complicated... and at each step there are several options, and each one could be optimal for some input strings but not for others.
E.g. if the range is 0-5000, you'd start by marking the 4-digit integers that are only present at one location. But after that, you could do the same thing again (because integers that were present twice could have had one of their options removed) until there's no more improvement, or you could check integers that are present twice, or integers that are present up to x times, or move on to 3-digit integers... I don't think there's a straightforward way to know which of these options will give the best result.
This solution should work for any input string and any start/end range:
We can think about the numbers in the string as a pool of digits that we can choose from. We start at startRange and go through to endRange, looking for each number along the way in our pool of digits.
When we find a number that can be composed from our pool of digits, we delete those digits from our pool of digits, as those digits are already being used to form a number in our range.
As soon as we come across a number that cannot be composed from our pool of digits, that must be the missing number.
const str = "14036587109"; // input
const numsLeft = str.split("").map(num => parseInt(num)); // array of numbers
const startRange = 0;
const endRange = 10;
for(let i = startRange; i <= endRange ; i++) {
// check if number can be formed given the numbers left in numsLeft
const numFound = findNum(numsLeft, i);
if(!numFound) {
console.log("MISSING: " + i); // prints 2
break;
}
}
function findNum(numsLeft, i) {
// array of digits
const numsToFind = String(i).split("").map(num => parseInt(num));
// default is true, if all digits are found in numsLeft
let found = true;
numsToFind.forEach(num => {
// find digit in numsLeft
const numFoundIndex = numsLeft.indexOf(num);
if(numFoundIndex < 0) {
// digit was not found in numsLeft
found = false;
return;
} else {
// digit was found; delete digit from numsLeft
numsLeft.splice(numFoundIndex, 1);
}
});
return found;
}
var input = '10436587109';
var range = [10,9,8,7,6,5,4,3,2,1,0];
var expr1 = new RegExp(range.join('|'),'g');
var expr2 = new RegExp('[0-9]','g');
var a = input.match(expr1).map(Number).concat(input.match(expr2).map(Number));
var x = range.filter(function(i){ return a.indexOf(i)===-1; });

Chunk a string every odd and even position

I know nothing about javascript.
Assuming the string "3005600008000", I need to find a way to multiply all the digits in the odd numbered positions by 2 and the digits in the even numbered positions by 1.
This pseudo code I wrote outputs (I think) TRUE for the odd numbers (i.e. "0"),
var camid;
var LN= camid.length;
var mychar = camid.charAt(LN%2);
var arr = new Array(camid);
for(var i=0; i<arr.length; i++) {
var value = arr[i]%2;
Alert(i =" "+value);
}
I am not sure this is right: I don't believe it's chunking/splitting the string at odd (And later even) positions.
How do I that? Can you please provide some hints?
/=================================================/
My goal is to implement in a web page a validation routine for a smartcard id number.
The logic I am trying to implement is as follows:
· 1) Starting from the left, multiply all the digits in the odd numbered positions by 2 and the digits in the even numbered positions by 1.
· 2) If the result of a multiplication of a single digit by 2 results in a two-digit number (say "7 x 2 = 14"), add the digits of the result together to produce a new single-digit result ("1+4=5").
· 3) Add all single-digit results together.
· 4) The check digit is the amount you must add to this result in order to reach the next highest multiple of ten. For instance, if the sum in step #3 is 22, to reach the next highest multiple of 10 (which is 30) you must add 8 to 22. Thus the check digit is 8.
That is the whole idea. Google searches on smartcard id validation returned nothing and I am beginning to think this is overkill to do this in Javascript...
Any input welcome.
var theArray = camid.split(''); // create an array entry for each digit in camid
var size = theArray.length, i, eachValue;
for(i = 0; i < size; i++) { // iterate over each digit
eachValue = parseInt(theArray[i], 10); // test each string digit for an integer
if(!isNaN(eachValue)) {
alert((eachValue % 2) ? eachValue * 2 : eachValue); // if mod outputs 1 / true (due to odd number) multiply the value by 2. If mod outputs 0 / false output value
}
}
I discovered that what I am trying to do is called a Luhn validation.
I found an algorithm right here.
http://sites.google.com/site/abapexamples/javascript/luhn-validation
Thanks for taking the time to help me out. Much appreciated.
It looks like you might be building to a Luhn validation. If so, notice that you need to count odd/even from the RIGHT not the left of the string.

Categories

Resources