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)
Related
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.
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
I have an object array that gets new values every time a new user is created. I need to do some search based on the person name and then do some operations with it and I implemented a binary search and in my code that I found in the internet but theres something thats bothering me with the search code.
The object looks as follows:
person = {
name: name,
password: password,
cartItems: '',
cartPrice: 0
}
then I push it to an array.
and the binary search code looks as follows:
searchValues(users, value) {
var startIndex = 0,
stopIndex = users.length,
middle = Math.floor((stopIndex + startIndex) / 2);
while(users[middle].name != value && startIndex < stopIndex){
//adjust search area
if (value < users[middle].name) {
stopIndex = middle - 1;
} else if (value > users[middle].name) {
startIndex = middle + 1;
}
//recalculate middle
middle = Math.floor((stopIndex + startIndex) / 2);
}
return (users[middle].name != value) ? -1 : middle;
}
My questions is: How does JavaScript do the comparison between string values, does it convert to ascii? I can understand the code if it was applied to numbers but I'm a bit confused when it comes to strings.
Thank you in advance for anyone willing to help
EDIT: I forgot to mention that i've sorted my array before hand.
The algorithm to compare two strings is simple:
Compare the first character of both strings.
If the first character from the first string is greater (or less) than the other string’s, then the first string is greater (or less) than the second. We’re done.
Otherwise, if both strings’ first characters are the same, compare the second characters the same way.
Repeat until the end of either string.
If both strings end at the same length, then they are equal. Otherwise, the longer string is greater.
reference find more detail here
In the function below, the for loop sometimes skips one iteration.
In some rare cases, I've also found it skipping two iterations.
This sometimes breaks my code and would probably affect my future codes, if my understanding of the for loop remains incomplete.
I further looked into the matter and tried the same with a while loop and found out that this problem doesn't happens if a while loop is used.
Why is the for-loop sometimes skipping some iterations ?
function forLoopString(len)
{
var string = 'abcdefghijklmnopqrstuvwxyz0123456789';
var character = '',
randomString = '';
for (var i = 0; i < len; i++)
{
character = string.charAt(Math.floor(Math.random() * string.length-1) + 0);
randomString += character;
}
if(randomString.length < len)
{
console.log('Less than required length!');
randomString = randomString + '5';
}
return randomString;
}
JSFiddle
The loop shown won't "skip" any iterations, but will iterate from [0, len) as told to do.
However, a negative argument to charAt makes it seem like it "skips" because "foo".charAt(-1) == "". The same empty-string result holds for any out-of-bounds to String.charAt:
.. If the index you supply [to charAt] is out of range, JavaScript returns an empty string.
A correction that yields an always-valid index would merely be Math.floor(Math.random() * string.length), without the -1.
Although this is slightly biased (for anyone that really cares) this is 'correct' because Math.random returns a number in the range [0, 1). Thus Math.random() * len returns a value from [0, len); and as an Integer in the same interval after the floor.
Also, it would be good to choose more useful variable names.. and, as Ed points out the +0 is irrelevant because Math.floor returns a (integer) number.
The random number is sometimes negative, that's why a character is skipper from randomString in those cases.
https://jsfiddle.net/ojbp0evz/3/
Use Math.abs for example.
Your problem is HERE:
character = string.charAt(Math.floor(Math.random() * string.length-1) + 0);
if your rand is less than 0, you will get a negative number, and therefor, you won't get any character. You must encapsulate your string.length-1 like so:
character = string.charAt(Math.floor(Math.random() * (string.length-1)));
Updated fiddle: DEMO
Always remember: MULTIPLICATIONS GOES FIRST!!
EDIT: string.length is 36, you dont need to substract 1 to it, just multiply
character = string.charAt(Math.floor(Math.random() * string.length));
My whole goal was to write a loop that would take a string, count the letters and return two responses: one = "this word is symmetric" or two = "this word is not symmetric". However the code I wrote doesn't console anything out. Here's the code:
var arya = function(arraycount){
for (arraycount.length >= 1; arraycount.length <= 100; arraycount++) {
while (arraycount.length%2 === 0) {
console.log("This is a symmetric word and its length is " + " " arraycount.length " units.");
arraycount.length%2 != 0
console.log("Not a symmetric word");
}
}
}
arya("Michael");
There are many ways to accomplish your goal, but here are a few. The first is a somewhat naïve approach using a for loop, and the second uses recursion. The third asks whether the string equals the reverse of the string.
iterative (for loop) function
var isPalindromeIteratively = function(string) {
if (string.length <= 1) {
return true;
}
for (var i = 0; i <= Math.floor(string.length / 2); i++) {
if (string[i] !== string[string.length - 1 - i]) {
return false;
}
}
return true;
};
This function begins by asking whether your input string is a single character or empty string, in which case the string would be a trivial palindrome. Then, the for loop is set up: starting from 0 (the first character of the string) and going to the middle character, the loop asks whether a given character is identical to its partner on the other end of the string. If the parter character is not identical, the function returns false. If the for loop finishes, that means every character has an identical partner, so the function returns true.
recursive function
var isPalindromeRecursively = function(string) {
if (string.length <= 1) {
console.log('<= 1');
return true;
}
var firstChar = string[0];
var lastChar = string[string.length - 1];
var substring = string.substring(1, string.length - 1);
console.log('first character: ' + firstChar);
console.log('last character: ' + lastChar);
console.log('substring: ' + substring);
return (firstChar === lastChar) ? isPalindromeRecursively(substring) : false;
};
This function begins the same way as the first, by getting the trivial case out of the way. Then, it tests whether the first character of the string is equal to the last character. Using the ternary operator, the function, returns false if that test fails. If the test is true, the function calls itself again on a substring, and everything starts all over again. This substring is the original string without the first and last characters.
'reflecting' the string
var reflectivePalindrome = function(string) {
return string === string.split('').reverse().join('');
};
This one just reverses the string and sees if it equals the input string. It relies on the reverse() method of Array, and although it's the most expressive and compact way of doing it, it's probably not the most efficient.
usage
These will return true or false, telling you whether string is a palindrome. I assumed that is what you mean when you say "symmetric." I included some debugging statements so you can trace this recursive function as it works.
The Mozilla Developer Network offers a comprehensive guide of the JavaScript language. Also, here are links to the way for loops and while loops work in JS.