Browser crashing while loop JavaScript algorithm - javascript

Guys i'm trying to write an algorithm where I pass in a large string and let it loop through the string and whatever palindrome it finds, it pushes into array but for some reason my browser is crashing once i put in the while loop and I have no
function arrOfPalindromes(str) {
var palindromeArrays = []
var plength = palindromeArrays.length
// grab first character
// put that in a temp
// continue and look for match
// when match found go up one from temp and down one from index of loop
// if matched continue
// else escape and carry on
// if palendrome push into array
var counter = 0;
for (var i = 0; i < str.length; i++) {
for (var j = 1; j < str.length - 1; j++) {
if (str[i + counter] === str[j - counter]) {
while (str[i + counter] === str[j - counter]) {
console.log(str[j], str[i])
// append the letter to the last index of the array
palindromeArrays[plength] += str[i]
counter++
}
}
}
}
return palindromeArrays
}
var result2 = arrOfPalindromes('asdfmadamasdfbigccbigsdf')
console.log(result2)

Do not mention about the algorithm but the condition
while (str[i + counter] === str[j - counter])
Make your code crash. A little surprise but str[j+counter] when j+counter > str.length return undefined and the same as j-counter <0. There for, your while loop never end because of undefined === undefined.

Returning same sized array to handle nested palis.
ex: abxyxZxyxab => 00030703000 odd numbered nested palis.
ex: asddsa => 003000 even numbered pali.
ex: asdttqwe => 00020000 i dont know if this is a pali but here we go
smallest pali is 2 char wide so i start at index:1 and increment till str.len-1
for (var i = 1; i < str.length-1; i++) {
counter=0;
while(str[i]+1-counter == str[i]+counter || str[i]-counter == str[i]+counter) { // always true when counter is 0
// while (even numbered palis || odd numbered palis)
// IF counter is bigger than 0 but we are still here we have found a pali & middle of the pali is i(or i+0.5) &size of the pali is counter*2(or+1)
if(str[i]+1-counter == str[i]+counter){//even sized pali
res[i]=counter*2;
}else{//odd sized pali
res[i]=counter*2+1;
}
counter++;//see if its a bigger pali.
}
}
not super optimized while + if,else checks same stuff. These can be somehow merged. Maybe even even and odd can be handled without any checks.

You don't need to use three loops. You can do it with two for loops where one starts from the beginning and other one is from the end of the string.
Here we use array reverse() method to match palindromes.
Also I added additional minLength parameter and duplication removal logic to make it more nice.
function findPalindromes(str, minLength) {
var palindromes = [];
var _strLength = str.length;
for (var i = 0; i < _strLength; i++) {
for (var j = _strLength - 1; j >= 0; j--) {
if (str[i] == str[j]) {
var word = str.substring(i, j + 1);
//Check if the word is a palindrome
if (word === word.split("").reverse().join("")) {
//Add minimum length validation and remove duplicates
if(word.length >= minLength && palindromes.indexOf(word) === -1){
palindromes.push(word);
}
}
}
}
}
return palindromes;
}
var result = findPalindromes('asdfmadamasdfbigccbigsdf', 2)
console.log(result)

Related

Find a secret word given an array of triplets?

My code works and I need help figuring out how to optimize it. If possible, don't give me any code, just tips on optimizing it, please.
The rules for the puzzle are:
Each triplet has the rules of how the letters are ordered in the secret word (each letter is followed by the next letter inside the triplet array).
all the letters of the secret word are distinct.
Example input:
triplets1 = [
['t','u','p'],
['w','h','i'],
['t','s','u'],
['a','t','s'],
['h','a','p'],
['t','i','s'],
['w','h','s']
]
Expected Output: "whatisup"
I came up with this code, which works. It selects the first letters that are not preceded by any others in the arrays they are placed, removes them, concatenates them into the final word, and keep doing that until all the arrays are empty. The code isn't being accepted though, because is exceeding the time limit.
function recoverSecret(triplets) {
let secretWord = '', character = '';
let notEmpty = true;
let size = triplets.length;
//it loops until array is empty
while(notEmpty) {
notEmpty = false;
for (let i = 0; i < size; i++) {
for (let j = 0; j < triplets[i].length; j++) {
if (character) j = 0; //everytime a character is included, this condition is truthy, so you have to go back to the start of the array because the character was removed last iteration
character = triplets[i][j];
let remove = []; //this array will have the positions of the letter to remove in the removal cycle
for (let k = 0; k < size; k++) {
if (character == triplets[k][0]) remove.push(k);
//if the letter is in the triplet and it's not the first position, then it isn't the letter we're looking for, so character equals to '', otherwise it will be the letter which will be added to the secretWord string
if (k != i && (triplets[k].includes(character))) {
if (character != triplets[k][0]) {
character = '';
break;
}
}
}
secretWord += character;
if (character) {
//if character is not '', then a removal loop is done to remove the letter because we just found its place
for (x of remove) {
triplets[x].shift();
}
}
// if (triplets[i].length == 0) break;
}
if (triplets[i] != 0) notEmpty = true; //if every triplet is empty, notEmpty remains false and while loop is over
}
}
return secretWord;
}
If possible I don't want any code, just tips on optimization, please.
After some thought, I got it right. The most important change was realizing that I didn't have to loop through each char in the array. I only needed to do it for the first char of each triplet sequence.
Then, made some other adjustments, like, instead of two ifs for checking if the char is the first element of every triplet it's in, I used the condition triplets[k].indexOf(character) > 0 to check both things at the same time, since indexOf method returns -1 if it doesn't find what it is looking for.
I'm not saying it's the best it could be, but now it is a little better in performance than it was
function recoverSecret(triplets) {
let secretWord = '', character = '';
let notEmpty = true;
while (notEmpty) {
notEmpty = false;
for (let i = 0; i < triplets.length; i++) {
if (triplets[i].length == 0) continue;
character = triplets[i][0];
let remove = [];
for (let k = 0; k < triplets.length; k++) {
if (triplets[k].indexOf(character) > 0) {
character = '';
break;
}
else if (triplets[k][0] == character) remove.push(k);
}
secretWord += character;
if (character) {
for (x of remove) {
triplets[x].shift();
}
}
if (triplets[i].length != 0) { notEmpty = true; }
console.log(triplets)
}
return secretWord;
}
}

Codewars Javascript Problem- my code with a double for loop times out

I'm trying to solve a problem on Codewars which involves seeing if one string includes all the letters in a second string. I think I've found a decent solution, but my code times out (12000ms) and I can't figure out why. Could anyone shed some light on this issue?
function scramble(str1, str2) {
let i;
let j;
let x = str2.split();
for (i = 0; i < str1.length; i++) {
for (j = 0; j < str2.length; j++) {
if (str1[i] == str2[j]) {
x.splice(j, 1);
j--;
}
}
}
if (x.length == 0) {
return true;
} else {
return false;
}
}
If your strings have sizes N and M then your algorithm is O(N*M). You can get O(NlogN + MlogM) by sorting both strings and then do a simple comparison. But you can do even better and get O(N+M) by counting the letters in one string and then see if they are present in the other. E.g. something like this:
function scramble(str1, str2) {
let count = {}
for (const c of str1) {
if (!count[c])
count[c] = 1
else
count[c]++
}
for (const c of str2) {
if (!(c in count))
return false
count[c]--
}
for (let k in count) {
if (count.hasOwnProperty(k) && count[k] !== 0)
return false
}
return true
}
You created an infinite loop by both incrementing and decrementing j. The value of j gets stuck whenever str1[i] == str2[j]
Reducing your code snippet to the simplest form would look something like this:
for (j = 0; j < 10; j++) {
j--;
console.log(j) // always -1
}
You're adjusting x but then referring to str2 as if it has been changed. Because you never adjust str2, you're always comparing the same two letters, so you get stuck in a loop. That's one problem. Then, your question's wording suggests that we're checking if every letter in str2 is in str1, but you're going through every letter in str1 and checking it against str2. str1 should be the inner loop.
function scramble(str1, str2) {
var x = str2.split("");
for (var i = 0; i < x.length; i++) {
for (var j = 0; j < str1.length; j++) {
if (str1[j] == x[i]) {
x.splice(i--, 1);
}
}
}
return x.length === 0;
}
console.log(scramble("dirty rooms", "dormitory"));
console.log(scramble("cat", "dog"));
Because x.length === 0 is already a boolean value, you can just return that. No need for the if statements there. The triple equals checks the variable's type and value, and double equals only checks value. I tend to always use triple when I'm checking against 0 and 1 because you don't want unintended consequences like this:
console.log(false == 0, true == 1);
console.log(false === 0, true === 1);
Also, because i-- means "i = i - 1 after execution", you can put that directly in your call to splice, and it won't execute until after splice is finished. --i, on the other hand, would be evaluated before execution.
This is all great, but using indexOf is a simpler solution:
function scramble(str1, str2) {
for (let i = 0; i < str2.length; i++) {
if (str1.indexOf(str2[i]) == -1) return false;
}
return true;
}
console.log(scramble("forty five", "over fifty"));
console.log(scramble("cat", "dog"));

reading 2D array behavior issue?

Test Cases:
(6, [1,3,2,6,1,2]) returns (pairs / 2) = 5
It does get the answer the thing is that is doing more than it needs to, It is possible just add some validation to know when he is adding same reversed index but it will only give it more to do. am looking to remove the unneeded work. can it be more reliable?
function returnOcurrences(k,ar){
debugger;
const letters = [new Set(ar)];
let pairs = 0;
for (let i = 0; i < ar.length; i++) {
for (let ii = 0; ii < ar.length; ii++) {
let a = ar[i] + ar[ii];
if (i != ii) {
if (a >= k) {
pairs += (a % k == 0) ? 1 : 0
}
}
}
}
return pairs/2;
}
What I assume you want to do is prevent the algorithm from checking e.g. ar[1] + ar[2], then ar[2] + ar[1] again.
To solve this, consider starting your inner loop from i + 1 and not from 0. This prevents the mentioned scenario from happening, and also prevents the algorithm from summing an element with itself (e.g. ar[0] + ar[0]). So no need to check anymore if i is equal to ii.
Why so? Assume the first iteration of the outer loop. You are checking the sum of ar[0] with ar[1], then with ar[2], ar[3], and so on.
On the second iteration of the outer loop, you are checking sums of ar[1] with other elements of the array. However, you already checked ar[0] + ar[1] in the previous iteration. So you start with ar[1] + ar[2], where ii = i + 1 = 2.
So the code becomes:
function returnOcurrences(k,ar){
const letters = [new Set(ar)]; //I don't see the purpose of this variable but okay
var pairs = 0;
for (var i = 0; i < ar.length; i++) {
for (var ii = i + 1; ii < ar.length; ii++) {
var a = ar[i] + ar[ii];
if (a >= k) {
pairs += (a % k == 0) ? 1 : 0
}
}
}
return pairs/2;
}
Hope this helps!

function is returning numbers instead of letters

I am trying to make a function that will return the last letter of each word in a string, and think I am close, but whenever I invoke the function, I get a series of numbers instead of the letters I am looking for.
This is the code:
function lastLetter(str){
var arr = [];
for(var i = 0; i < str.length; i++){
if(str[i] === " " || str[i] === "."){
arr.push((i) - 1);
}
}
return arr;
}
lastLetter("I love bacon and eggs.");
Any advice would be appreciated. Thanks!!
You push the value i - 1 onto the array. You meant to push str.charAt(i-1):
arr.push(str.charAt(i - 1));
See: String charAt().
Note that your code isn't really defensive. If there is a space or period at the first character in the string, you are referencing the character at position -1, which is not valid. You could solve this by looping from 1 instead of 0. In that case you would still get a space in the result if the string starts with two spaces, but at least you won't get an error. A slightly better version of the algorithm would test if i-1 is a valid index, and if there is a character at that position that is not a space or a period.
Below is a possible solution, which I think solves those cases, while still retaining the structure of the code as you set it up.
function lastLetter(str){
var arr = [];
for(var i = 1; i < str.length; i++){
var p = str.charAt(i-1);
var c = str.charAt(i);
if ( (c === " " || c === ".") &&
!(p === " " || p === ".") ) {
arr.push(p);
}
}
return arr;
}
console.log(lastLetter("... Do you love bacon and eggs..."));
Try:
arr.push(str[i - 1]);
This will have problems with multi-byte characters, however.
You're pushing the integer value i-1 rather than the character str[i-1].
You are pushing the index into your array. You still need to access i-1 of your string
`
` function lastLetter(str){
var arr = [];
for(var i = 0; i < str.length; i++){
if(str[i] === " " || str[i] === "."){
arr.push(str[(i) - 1]);
}
}
return arr;
}
lastLetter("I love bacon and eggs.");
Solution and Improved version below:
use arr.push(str.charAt(i - 1)) instead of arr.push((i) - 1)
(You where saving the position of the last letter, but not it's value - charAt(position) does give you the latter at the given position
charAt(): http://www.w3schools.com/jsref/jsref_charat.asp
Demo:
function lastLetter(str){
var arr = [];
for(var i = 0; i < str.length; i++){
if(str[i] === " " || str[i] === "."){
arr.push(str.charAt(i - 1));
}
}
return arr;
}
document.body.innerHTML = lastLetter("I love bacon and eggs.");
Improved Version:
function lastLetter(str) {
var arr = [];
var words = str.split(/[\s\.?!:]+/)
for (var i = 0; i < words.length; ++i) {
if (words[i].length > 0) {
var lastLetter = words[i].charAt(words[i].length - 1)
arr.push(lastLetter);
}
}
return arr;
}
document.body.innerHTML = lastLetter("... Is this: correct?! I love bacon and eggs.");

Finding Least Common Multiple using Table Method

Find the least common multiple of the provided parameters using Table Method that can be evenly divided by both, as well as by all sequential numbers in the range between these parameters. There will only be two parameters. For ex [1,3], find the lcm of 1,2,3.
Note - It might create an infinite loop
function smallestCommons(arr) {
var nums = [];
var multiples = [];
if(arr[0]>arr[1]) {
var bigger = arr[0];
} else {
var bigger = arr[1];
}
for(var i=bigger;i>0;i--) {
nums.push(i);
console.log(i);
}console.log(nums + " nums");
var sums = 0;
while(sums != nums.length) {
for(var k=0;k<nums.length;k++) {
if(nums[k] % 2 === 0) {
nums[k] = nums[k]/2;
multiples.push(2);
} else if(nums[k] % 3 === 0) {
nums[k] = nums[k]/3;
multiples.push(3);
}else if(nums[k] % 5 === 0) {
nums[k] = nums[k]/5;
multiples.push(5);
}else if(nums[k] % 7 === 0) {
nums[k] = nums[k]/7;
multiples.push(7);
}else if(nums[k] === 1) {
break;
}else {
nums[k] = nums[k]/nums[k];
multiples.push(nums[k]);
}
}
for(var j = bigger; j>0;j--) {
sums = sums + nums[j];
}
}
var scm = [multiples].reduce(function(a,b){console.log(a*b)}); return scm
}
smallestCommons([1,5]);
I found this to be a simple solution, It works wonders;
Loop through all possible numbers, beginning with lower bound input (var i)
for every number, test divisibility by each number between and including input bounds (var j)
if i meets all criteria return it as answer, otherwise increment i by 1 and try again
click here for explanation of ? operator in variable initialization
function smallestCommons(arr) {
//set variables for upper and lower bounds
//incase they aren't entered in ascending order
var big = arr[0] < arr[1] ? arr[1]:arr[0],
small = arr[0] < arr[1] ? arr[0]:arr[1],
i = small;
//loop through all numbers, note the possibility of an infinite loop
while(true){
//test each number for divisibility by by both upper and lower
//bounds, as well as by all sequential numbers inbetween
for(var j = small; j <= big; j++){
if(i % j === 0){
if(j===big){
return i;
}
}else {
break;
}
}
i++;
}
}
smallestCommons([1,5]); //60
What you need is find the LCM in range (n, m) ?
Finding least common multiples by prime factorization seems better.
You can use Legendre's formula to find all prime factors of n! and m! , then just do a simple subtraction.

Categories