Explain the Javascript code, pad to left or rightside of string - javascript

I am learning Javascript and trying to do some challenges on codewar. I have the code for a challenge and I am trying to understand the logic.
The code snippet of interest is the function padIt, which accepts 2 parameters:
str - a string we need to pad with "*" at the left or rightside
n - a number representing how many times we will pad the string.
My question is, why do they use n-- and not n++?
function padIt(str, n) {
while(n>0) {
if(n%2 === 0) {
str = str + "*";
}
else{
str = "*" + str;
}
n --;
}
}

if you use n++ the while loop will never end since it is checking if n is larger than 0
imagine n is 3: n will be 4,5,6,7,8 then it is a infinite while loop
instead n represent how many times to pad the string so if you want to add 3 * n will go down from 3 to 2 to 1 and the while loop will end

Why n-- and not n++?
First of all notice the condition of while loop n>0. It means keep executing the while block. Means keep padding the string until n is greater than 0. Initially n is always greater than 1. So we need to decrease is in order to end the while loop.
If we use n++ instead of n-- the code will create infinite loop

Related

How to implement an algorithm to detect if a number has 2 consecutive digits?

I want to create a function that returns true if a number has consecutive digits or not,
example:
if the input is 11, it will return true
if the input is 21 it will return false
if the input is 323 it will return false because even though we have 3 repeated, they are not consecutive
My solution right now is to transform the number into an array and loop through the number one by one, if the next number is equal to the current number then we just return true. But this has a complexity time of O(n) and I was wondering if anyone can come with a better solution.
Thank you
There is an arguably better solution where you don't need to convert the number into a string or array of numbers/character. It works as follows:
Initialize a variable curr to -1.
Run a loop while num > 0 and do the following:
next_curr = num % 10
if next_curr == curr: return true
curr = next_curr
num = num / 10 (integer division)
If the loop completes, return false.
This is a one pass O(log n) time complexity algorithm where n is the input number. The space complexity is O(1)
Note that while your algorithm was also O(log n) time complexity, it did 2 passes, and had a space complexity of O(log n) too.
I haven't written JS for some time now, but here's a possible implementation of the above algorithm in JS:
function sameAdjacentDigits(num) {
// to deal with negative numbers and
// avoid potential problems when using Math.floor later
num = Math.abs(num)
let curr = -1
while (num > 0) {
const nextCurr = num % 10
if (nextCurr == curr) return true
curr = nextCurr
num = Math.floor(num / 10)
}
return false
}
Use some regex, and then check what was found via the matcher
numbers_match = /(00|11|22|33|44|55|66|77|88|99)/;
numbers_match.match("11")
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match
Easiest way to execute this is by using regex. Not sure what would be effectiveness of algorithm, but solution could be
/(\d)\1/
Inspired from #Tschallacka's answer:
let numbers = [11,21,323];
let result = numbers.map(n=>{
let test = n.toString().match(/(00|11|22|33|44|55|66|77|88|99)/);
return test != null;
})
console.log(result);

Having a tough time understanding longestPalindrome algorithm

I found this solution to make sense to an algorithm question about finding the longest palindrome in a substring. However, I am struggling to understand what the expand function is actually doing. I thought it would run from the center, but console logging is showing that it is run for the whole string. I am having difficulty understanding why it's entering the while loop for any character that's not the same as s[begin] === s[end] that's what I thought this line was preventing. I am not sure why expand is called twice either. Also, why do we add begin + 1 rather than just begin when returning the substring. The code is below. Any clarification of how the expand works would be appreciated.
var longestPalindrome = function (s) {
//either when empty string or is a single character
if (!s || s.length <= 1) return s
let longest = s.substring(0, 1)
for (let i = 0; i < s.length; i++) {
let temp = expand(s, i, i, 'first')
// console.log(temp, 'i am the first')
if (temp.length > longest.length) {
longest = temp
}
temp = expand(s, i, i + 1, 'second')
// console.log(temp, 'i am the second')
if (temp.length > longest.length) {
longest = temp
}
}
return longest
}
const expand = (s, begin, end, counter) => {
while (begin >= 0 && end <= s.length - 1 && s[begin] === s[end]) {
console.log(s, 'i am string')
console.log(begin, 'i am begin')
console.log(end, 'i am begin')
console.log(s[begin], s[end])
console.log(counter)
begin--
end++
}
return s.substring(begin + 1, end)
}
console.log(longestPalindrome('cbbd'))
console.log(longestPalindrome('babad'))
console.log(longestPalindrome('ac'))
console.log(longestPalindrome('abb'))
Well, let's say we want to find the palindrome of s when s = 'abba'.
note: Think of expansion as a palindrome finder that takes in the center index. Keep in mind that I'll explain how does expansion work afterwards
We'll shift the center of the expansion from 0 to 3(the final index). First, it checks the the expansion from begin = 0 and end = 0.
s = 'abba'
If begin = 0 and end = 0, expansion returns 'a'.
v___
abba //only the palindrome from 0 is 'a'
If begin = 0 and end = 1, expansion returns ' '.
_v___
a bba //there are no palindrome from between 0 and 1
If begin = 1 and end = 1, expansion returns 'b'.
_v__
abba //only the palindrome from 1 is 'b'
If begin = 1 and end = 2, expansion returns 'abba'.
__v__
ab ba //the palindrome from between b and b is 'abba'
If begin = 2 and end = 2, expansion returns 'b'.
If begin = 2 and end = 3, expansion returns ' '.
If begin = 3 and end = 3, expansion returns 'a'.
At this point, we've tested all the possible palindrome of s. Finally, the code returns the longest palindrome so far, which will be 'abba' in this case.
Notice why we need to perform expansion twice. At i = 1, we can look for a palindrome from the center.
_v__
abba
But also from the place between b and b
__v__
ab ba
Former returns 'b' while latter returns 'abba'. This is why you need to perform 2 expansions for each i.
Now, what exactly happens inside the expand function?
Let's use the example. s = 'aacdeedcba', and begin = 4 and end = 5.
First, the while loop checks if s[begin] == s[end].
____vv____
aacdeedcba
Obviously 'e' === 'e' is true, so we'll shift begin to left side, and end to right side .
Again we'll check if s[begin] === s[end].
___v__v____
aacdeedcba
Again, 'd' === 'd' is true, so we'll shift begin to left side, and end to right side .
we'll check again.
__v____v__
aacdeedcba
Again, 'c' === 'c' is true, so we'll shift begin to left side, and end to right side .
we'll check again.
_v______v_
aacdeedcba
This time, 'a' === 'b' is not true. So the while loop is stopped. And the expansion function returns 'cdeedc'.
However, I am struggling to understand what the expand function is actually doing. I thought it would run from the center, but console logging is showing that it is run for the whole string
Palindromes can be of two types
1. Even length palindromes e.g. - "abba"
2. Odd length palindromes e.g. - "aba"
Now, each index of the given string can be center of either of these types of palindromes.
So, every index of the string is tested for this possibility.
longestPalindrome function takes the string and for each index of the string it tests that index as center of an odd length palindrome and an even length palindrome.
expand function takes the two center indices and begins expanding them outwards. For an odd length palindrome, the center character should be compared with itself. So, expand is called with both begin and end as same index.
I am having difficulty understanding why it's entering the while loop for any character that's not the same as s[begin] === s[end] that's what I thought this line was preventing.
The longestPalindrome will send the two indices such that this condition s[begin] === s[end] is not true but the while loop will check and not expand them further. You can prevent this functionality from the longestPalindrome function if you want to.
I am not sure why expand is called twice either.
It is because of the two types of palindromes on the basis of their length. You need to test for both these types.
Also, why do we add begin + 1 rather than just begin when returning the substring.
It is because begin is one off when if comes out of the while loop. You can do a simulation on paper to understand better.

Print all possible strings that can be made by placing spaces

I came across this problem.
Print all possible strings that can be made by placing spaces.
I also came across this solution.
var spacer = function (input) {
var result = '' ;
var inputArray = input.split('');
var length = inputArray.length;
var resultSize = Math.pow(2,length-1); // how this works
for(var i = 0 ; i< resultSize ; i++){
for(var j=0;j<length;j++){
result += inputArray[j];
if((i & (1<<j))>0){ // how this works
result += ' ' ;
}
}
result += '\n' ;
}
return result;
}
var main = function() {
var input = 'abcd' ;
var result = spacer(input);
console.log(result);
}
main();
I am not getting how the marked lines work?
Can you clarify on what technique is being used? And what is the basic logic behind this? What are some of other areas where we can use this?
Thanks.
Let's take string abcd as an example.
There are 3 possible places where space can be put:
Between "a" and "b"
Between "b" and "c"
Between "c" and "d"
So generally if length of your string is length then you have length - 1 places for spaces.
Assume that each such place is represented with a separate digit in a binary number. This digit is 0 when we don't put space there, and 1 when we do. E.g.:
a b c d
0 0 0 means that we don't put any spaces - abcd
0 0 1 means that we put space between "c" and "d" only - abc d
0 1 0 means that we put space between "b" and "c" only - ab cd
0 1 1 means ab c d
1 0 0 means a bcd
1 0 1 means a bc d
1 1 0 means a b cd
1 1 1 means a b c d
Converting 000, 001, 010, ..., 111 from binary to decimal will give us values 0, 1, 2, ..., 7.
With 3 places for spaces we have 8 options to put them. It's exactly 2^3 or 2^(length - 1).
Thus we need to iterate all numbers between 0 (inclusive) and 2^(length - 1) (exclusive).
Condition (i & (1 << j)) > 0 in the code you provided just checks whether digit at position j (starting from the end, 0-based) is 0 (and we don't need to insert space) or 1 (and space should be added).
Let's take value 6 (110 in binary) for example.
(6 & (1 << 0)) = 110 & 001 = 000 = 0 (condition > 0 is not met)
(6 & (1 << 1)) = 110 & 010 = 010 = 2 (condition > 0 is met)
(6 & (1 << 2)) = 110 & 100 = 100 = 4 (condition > 0 is met)
simple solution Using Javascript
String.prototype.splice = function(idx, rem, str) {
return this.slice(0, idx) + str + this.slice(idx + Math.abs(rem));
};
function printPattern(str, i, n){
if(i==n){
return;
}
var buff = str;
var j = str.length - n + i;
buff = str.splice(j,0," ");
console.log(buff);
printPattern(str, i+1, n);
printPattern(buff, i+1, n);
}
var str = "ABCD"
printPattern(str, 1, str.length);
console.log(str);
There are 2 possibilities between each 2 characters: either has space or not. If spaces are allowed only between characters then the number of possibilities for 4 characters is 2 * 2 * 2 or 2 ^ (length - 1)
resultSize = Math.pow(2, length - 1) says there are 2^n-1 possible ways to print a string given the problem definition. As far as to why that is the number of solutions is pretty easy to understand if you start with a string that has 2 characters and work your way upward. So pretend you have the string "ab". There is two solutions, you can put a space between a and b or you can not put a space between a and b. Lets add a character to get "abc". Well, we already know that there are two solutions for the string "ab" so we need multiply that solution by the number of ways you can put a space between b and c. Which is 2 so that gives us 2 * 2 solutions. You can extend that to a string of n size. So that means that the number of solutions is 2 * 2 * 2 * 2 * ... n - 1. Or in otherwords 2 ^ n-1.
The next part is a little trickier, but its a clever way of determining how many spaces and where they go in any given solution. The bitwise & takes the each bit of two numbers, compares them, then spits out a new number where each bit is a 1 if both the bits of the two numbers were 1, or 0 if the bits were not 1 and 1. For example (binary numbers):
01 & 01 = 01
10 & 01 = 00
Or a bigger example:
10010010 & 10100010 = 10000010
The << operator just moves all bits n position to left where n is the right hand expression (multiply by 2 n times).
For example:
1 << 1 = 2
2 << 1 = 4
2 << 2 = 8
1 << 4 = 8
So back to your code now. Lets break down the if statement
if(i & (1 << j) > 0){ ... }
In english this says, if the number index of the solution we are looking at shares any 1 bits with 1 shifted by the index of the character we are looking at, then put a space after that character. Olexiy Sadovnikov's answer has some good examples of what this would look like for some of the iterations.
Its also worth noting that this is not the only way to do this. You could pretty easily determine that the max number of spaces is n - 1 then just linearly find all of the solutions that have 0 spaces in them, then find all the solutions that have 1 space in them, then 2 spaces .... to n - 1 spaces. Though the solution you posted would be faster than doing it this way. Of course when your talking about algorithms of exponential complexity it ultimately won't matter because strings bigger than about 60 chars will take longer than you probably care to wait for even with a strictly 2^n algorithm.
In answer to the question of how these techniques can be used in other places. Bit shifting is used very frequently in encryption algorithms as well as the bitwise & and | operators.
'''
The idea was to fix each character from the beginning and print space separated rest of the string.
Like for "ABCD":
A BCD # Fix A and print rest string
AB CD # Add B to previous value A and print rest of the string
ABC D # Add C to previous value AB and print rest of the string
Similarly we can add a space to produce all permutations.
Like:
In second step above we got "AB CD" by having "A" as prefix
So now we can get "A B CD" by having "A " as a prefix
'''
def printPermute(arr, s, app):
if len(arr) <= 1:
return
else:
print(app +''+arr[0:s] +' '+ arr[s:len(arr)])
prefix = app + ''+arr[0:s]
suffix = arr[s:len(arr)]
printPermute(suffix, 1, prefix)
printPermute(suffix, 1, prefix+' ') #Appending space
printPermute("ABCDE", 1, '') #Empty string
.pow is a method from Math which stands for "power". It takes two arguments: the base (here 2) and the exponent. Read here for more information.
& is the bitwise AND operator, it takes the two binary representation of the numbers and performs a logical AND, here's a thread on bitwise operators
EDIT: why Math.pow(2,length-1) gives us the number of possible strings?
I remember doing it in an exercise last year in math class, but I'll try to explain it without sums.
Essentially we want to determine the number of strings you can make by adding one or no space in between letters. Your initial string has n letters. Here's the reason why:
Starting from the left or the word after each letter you have two choices
1 - to put a space
2 - not to put a space
You will have to choose between the two options exactly n-1 times.
This means you will have a total of 2^(n-1) possible solutions.

Finding the longest palindrome

I got this to work after tinkering with it, but am unclear on some of the -1s and +1s going on. my code below as well as my questions in the comments. Also recapping the questions up here:
In the return statement inside the "centeredPalindrome" helper function why is it "left + 1"? Is that because you are expanding but only care about what's "in the expansion", not the outer limit?
In that same return statement, why right and not right +1? is it because you are doing "length" and NOT "length-1" in the while condition?
if it is odd, we expand one extra to the left - why? is it because an odd palindrome will always have "one extra at the beginning"?
var longestPalindrome = function(string){
var length = string.length;
var result = "";
//helper function
var centeredPalindrome = function(left, right){
//while loop with conditions for it being a palindrome. iterate to left/right while those conditions are met:
while(left>= 0 && right < length&& string[left] === string[right]){
left--;
right++;
}
//why right and not right + 1? is it because you are doing length (and NOT length -1) in the while loop?
//why left + 1? Is that because you are expanding but only care about what's "in the expansion", not the outer limit?
return string.slice(left + 1, right);
}
//iterate through the string and apply the helper function
for (var i = 0; i < string.length; i++) {
//handle case for it being odd or even
var evenPal = centeredPalindrome(i, i);
// if it is odd, we expand one extra to the left via "i -1" - why? is it because an odd palindrome will always have "one extra at the beggining"?
var oddPal = centeredPalindrome(i-1, i);
//overwrite the result with the longest one between them
if(oddPal.length > result.length){
result = oddPal;
}
if(evenPal.length > result.length){
result = evenPal;
}
};
//return the final result
return result;
}
console.log(longestPalindrome("racecar"));
// returns "racecar" if I change the return inside "centerPalindrome" to string.slice(left, right), this becomes:
//"ra"
console.log(longestPalindrome("radar")); // returns "radar"
console.log(longestPalindrome("abba")); // returns "abba"
probably better to name the variables like this per #DrewGaynor:
var oddPal = centeredPalindrome(i - 1, i + 1);
var evenPal = centeredPalindrome(i, i + 1);
in the case of the odd palindrome, you want to look to the left and to the right of the center, as below.
var oddPal = centeredPalindrome(i - 1, i + 1);
racecar
^
|
Center is one character because the string has an odd length (7 characters)
in the case of the even palindrome, you want to look at the center which is two chars long, to do this, you need to account for the extra length of the center.
you could have also done i-1 for the "left" instead of i+1 for the "right".
but you don't want to do it for both since then you will be looking at a three letter center or starting the left at -1!
var evenPal = centeredPalindrome(i, i + 1);
abba
^^
|
Center is two characters because the string has an even length (4 characters)
In the return statement inside the "centeredPalindrome" helper function why is it "left + 1"? Is that because you are expanding but only care about what's "in the expansion", not the outer limit?
In that same return statement, why right and not right +1? is it because you are doing "length" and NOT "length-1" in the while condition?
The left and right variables are the left and right outer bounds of the substring. The desired output is everything between those bounds. To illustrate:
abcd_racecar-efgh
^ ^
| |
| Final value of "right" (12)
Final value of "left" (4)
The arguments of the slice function are the start index (inclusive, so the actual index of the start of the desired output) and end index (exclusive, so the index immediately following the end of the desired output). The right value is already where we want it, but the left value needs to be incremented by 1 to be correct.
"abcd_racecar-efgh".slice(5, 12); //Output is "racecar"
If it is odd, we expand one extra to the left - why? is it because an odd palindrome will always have "one extra at the beginning"?
This is done because the center of the palindrome could be two characters if it has an even length, or one character if it has an odd length (which actually seems to me to contradict the variable names). To illustrate:
racecar
^
|
Center is one character because the string has an odd length (7 characters)
abba
^^
|
Center is two characters because the string has an even length (4 characters)

Combinatorial Optimisation with 0 and 1

I'm working on a way to solve this problem :
Given an amount of 0 and 1, generate the list containing all the possible combinations of 0 and 1.
Example : if we have one 1 and two 0, the algorithm will return
001
010
100
The problem is that the classic combination algorithms are not optimized for this purpose.
This is the result a non optimized combination algorithm would return :
001
001
010
010
100
100
As you can see, all combinations are repeated twice because 0 are interpreted as different elements.
How an algorithm can be made to generate the list of possible combination inputting the number of 0 and 1, without repeating combination ?
PS : This is will be used in Javascript
EDIT : Solved ! Using #Batch method.
function combin(o, i)
{
if (o == 0 && i > 0)
{
for (var j = 0, s = ''; j < i; j++)
{
s += '1';
}
return [s];
}
else if (i == 0 && o > 0)
{
for (var j = 0, s = ''; j < o; j++)
{
s += '0';
}
return [s];
}
else if (i == 0 && 0 == o)
{
return [''];
}
else if (i > 0 && o > 0)
{
var l = combin(o - 1, i);
for (var j in l)
{
l[j] = '0' + l[j];
}
var k = combin(o, i-1);
for (var j in k)
{
k[j] = '1' + k[j];
}
return l.concat(k);
}
}
Here's one way to do it: start out with the string putting all 0s to the front. Now shift the rightmost 0 through to the end. Afterwards, shift the second rightmost 0 one position to the right, put the last 0 right next to it. Again, shift the rightmost 0 through to the end. Shift the second rightmost 0 another step right, put the rightmost 0 next to it again, shift shift shift. Once you shift the pair of the two rightmost 0s, start shifting the third right most 0.... you get the picture.
Example: three 0s, three 1s:
000111
001011
001101
001110
010011
010101
010110
011001
011010
011100
100011
100101
100110
101001
101010
101100
110001
110010
110100
111000
Not sure how to write a nice loop for that, but once you grasp the idea, you can play around and try to figure one out if you'd like. Maybe you can find a nifty recursion though, can't think of one for this method right now though.
A more elegant approach would be the following; notice that the ways to generate all strings with n 0s and m 1s is:
Start the string with a 0, append all combinations of generating strings from n-1 0s and m 1s, or start the string with a 1 and append all combinations of generating strings from n 0s and m-1 1s. The sets of generated strings are disjoint, so a simple recursion will do, no worrying about strings being generated multiple times necessary. If you prefer, you can do it iteratively as well (iterate over the length of the generated string for that, for iteration i, keep sets for no 0 used, one 0 used, ..., i 0s used, etc.)
All in all, recursion seems the way to go to me. Base cases are simple: if n = 0, the only string you can get is 1^m, if m = 0 the only string you can get is 0^n (where ^ denotes repetition).
In case you want to implement that recursion and also a way to test it (to some degree), notice that the number of strings you can produce is n + m choose n = n + m choose m, so counting the number of strings you get will give you a hint whether what you're doing works as intended. Not sure whether javascript offers easy access to binomial coefficients.
I assume you want to do this fast. If you represent the 0/1 pattern as bits of an integer, the smallest one is "0...01..1" e.g. 0001111 if you have 3 0-bits and 4 1-bits - and the largest integer is "1..10..0". (e.g. 1111000 ).
Your problem is then a known problem of generating the lexicographically next bit permutation (i.e. the next integer with same number of 1 bits).
A simple implementation using code from http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation is the following (in pseudocode):
v = '0..01..1'b // Initialize t as smallest integer of the pattern
while ( t <= '1..10..0'b ):
t = (v | (v - 1)) + 1
v = t | ((((t & -t) / (v & -v)) >> 1) - 1)
print v
see http://www.geeksforgeeks.org/next-higher-number-with-same-number-of-set-bits/ and references therein for a more detailed explanation on how the algorithm works.

Categories

Resources