Having a tough time understanding longestPalindrome algorithm - javascript

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.

Related

A task in JavaScript

I need to create a sequence of numbers using while or for that consists of the sum of the symbols of the number.
For example, I have a sequence from 1 to 10. In console (if I've already written a code) will go just 1, 2,3,4,5,6,7,8,9,1. If I take it from 30 to 40 in the console would be 3,4,5,6,7,8,9,10,11,12,13.
I need to create a code that displays a sum that goes from 1 to 100. I don't know how to do it but in console I need to see:
1
2
3
4
5
5
6
7
8
9
1
2
3
4
etc.
I've got some code but I got only NaN. I don't know why. Could you explain this to me?
for (let i = '1'; i <= 99; i++) {
let a = Number(i[0]);
let b = Number(i[1])
let b1 = Boolean(b)
if (b1 == false) {
console.log ('b false', a)
}
else {
console.log ('b true', a + b)
}
}
I hope you get what I was speaking about.
Although I like the accepted answer however from question I gather you were asking something else, that is;
30 become 3+0=3
31 become 3+1=4
37 becomes 3+7=10
Why are we checking for boolean is beyond the scope of the question
Here is simple snnipet does exactly what you ask for
for (let i = 30; i <= 40; i++) {
let x=i.toString();
console.log( 'numbers from ' +i + ' are added together to become '+ (Number(x[0])+Number((x[1])||0)))
}
what er are doing is exactly what Maskin stated begin with for loop then in each increment convert it to string so we can split it, this takes care of NAN issue.
you don't need to call to string just do it once as in let x then simply call the split as x[0] and so on.
within second number we have created a self computation (x[1])||0) that is if there is second value if not then zero. following would work like charm
for (let i = 1; i <= 10; i++) {
let x=i.toString();
console.log( 'numbers from ' +i + ' are added together to become '+ (Number(x[0])+Number((x[1])||0)))
}
Did you observe what happens to ten
here is my real question and solution what if you Don't know the length of the digits in number or for what ever reason you are to go about staring from 100 on wards. We need some form of AI into the code
for (let i = 110; i <= 120; i++) {
let x= Array.from(String(i), Number);
console.log(
x.reduce(function(a, b){ return a + b;})
);
};
You simply make an array with Array.from function then use simple Array.reduce function to run custom functions that adds up all the values as sum, finally run that in console.
Nice, simple and AI
You got NaN because of "i[0]". You need to add toString() call.
for (let i = '1'; i <= 99; i++) {
let a = Number(i.toString()[0]);
let b = Number(i.toString()[1])
let b1 = Boolean(b)
if (b1 == false) {
console.log('b false', a)
} else {
console.log('b true', a + b)
}
}
So the way a for loop works is that you declare a variable to loop, then state the loop condition and then you ask what happens at the end of the loop, normally you increment (which means take the variable and add one to it).
When you say let i = '1', what you're actually doing, is creating a new string, which when you ask for i[0], it gives you the first character in the string.
You should look up the modulo operator. You want to add the number of units, which you can get by dividing by 10 and then casting to an int, to the number in the tens, which you get with the modulo.
As an aside, when you ask a question on StackOverflow, you should ask in a way that means people who have similar questions to you can find their answers.

How does javascript do the comparison?

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

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)

counting a word and returning a whether it is symmetric or not in Javascript

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.

How to read letter by letter in textfield using javascript

I want to verify a Singapore NRIC using javascript,
the formula is
1) Take for example I want to test the NRIC number S1234567. The first digit you multiply by 2, second multiply by 7, third by 6, fourth by 5, fifth by 4, sixth by 3, seventh by 2. Then you add the totals together. So,1×2+2×7+3×6+4×5+5×4+6×3+7×2=106.
2) If the first letter of the NRIC starts with T or G, add 4 to the total.
3) Then you divide the number by 11 and get the remainder. 106/11=9r7
4) You can get the alphabet depending on the IC type (the first letter in the IC) using the code below:
If the IC starts with S or T: 0=J, 1=Z, 2=I, 3=H, 4=G, 5=F, 6=E, 7=D, 8=C, 9=B, 10=A
If the IC starts with F or G: 0=X, 1=W, 2=U, 3=T, 4=R, 5=Q, 6=P, 7=N, 8=M, 9=L, 10=K
How can I read every single number to do the formula?
Thanks in advance!
Strings are arrays so you can assign variables to the letters assuming you know the string has a fixed length, which it appears so, and then create a function to evaluate the characters and do the correct thing:
var nric = 'S1234567'
var chars = {
letter: nric[0],
one: nric[1],
...
}
string.charAt(charpos)
Should do the trick.
Alternatively,
string.substr(charpos,1)
You could also do
string[charpos]
but the first two above are recommended. This will return undefined if the string is empty, whereas the other alternatives will all return the null string.
Another idea is
string.split("")[charpos]
Finally, there is
string.slice(charpos,1)
Since you want a single character at a time I'd recommend charAt.
Elaborating on #elclanrs answer:
var nric = 'S1234567'
var numVal = 0; for(var i = 1, bound = nric.length; i < bound; i++){
numVal += parseInt(nric[i]);
}
if(nric[0] === 'S' && numVal === 28){
alert('ok');
}else{
alert('wrong');
}
should more or less do the trick.
I trust, You can do the multiplying yourself :-)

Categories

Resources