Time out error (array is subsequence of another array) - javascript

I wrote this code in my computer's IDE and passed all test cases I could think of. However, after testing it out on AlgoExpert and LeetCode, I'm getting a Time Out error. I've wracked my brain trying to come up any test cases I could've missed, but cannot seem to think of any. I've also tried testing out all the test cases they give me in AlgoExpert. Hopefully I'm not missing anything obvious... Any tips from someone will be much appreciated!
Given 2 non-empty array of integers, write a function that determines whether the second array is a subsequence of the first. Note that a single number in an array or the array itself are both valid subsequences of the array.
function isValidSubsequence(array, sequence) {
if (sequence.length > array.length) return false;
let s = 0;
let a = 0;
while (s < sequence.length){
if (array[a] === sequence[s]){
s++;
a++
}
else a++;
if (s !== sequence.length - 1 && a === array.length - 1) return false;
}
return true;
}

Your algorithm does not return at the end if the sequence fails.
// running this will freeze your browser
function isValidSubsequence(array, sequence) {
if (sequence.length > array.length) return false;
let s = 0;
let a = 0;
while (s < sequence.length){
if (array[a] === sequence[s]){
s++;
a++
}
else a++;
if (s !== sequence.length - 1 && a === array.length - 1) return false;
}
return true;
}
console.log(isValidSubsequence([1, 2, 3], [5]));
For what you're trying to do, you probably want a nested loop to check if the sequence matches, starting at an index in the array (starting the sequence index from 0):
function isValidSubsequence(array, sequence) {
let s = 0;
outer:
while (s < array.length){
for (let i = 0; i < sequence.length; i++) {
if (array[s + i] !== sequence[i]){
s++;
continue outer;
}
return true;
}
s++;
}
return false;
}
console.log(isValidSubsequence([1, 2, 3], [5]));
But I think .includes would be easier:
const isValidSubsequence = (array, sequence) => array.join().includes(sequence.join());
console.log(isValidSubsequence([1, 2, 3], [5]));
console.log(isValidSubsequence([1, 2, 3], [2, 3]));

Related

How do i use while loop while figuring out Fibonacci series?

I have just started with Js, I was facing an issue with Js, and could not find a similar solution, would be grateful if anyone helps.
var i = 0;
var j = 1;
var k = 0;
function fibbo(n) {
if (n === 1) {
console.log([0]);
} else if (n === 2) {
console.log([0, 1]);
} else {
while (k <= n) {
var l = [0, 1];
var b = l.length;
l.push(l[b - 2] + l[b - 1]);
k++;
}
}
return l;
}
fibbo(4);
console.log(l);
Make sure to declare variables locally inside the function, if that is where they are used. You can deal with the base cases differently (setting the length property), and there is the recent JS method Array#at... :
function fibo(n) {
const l = [0, 1];
while (l.length < n) l.push(l.at(-2) + l.at(-1));
l.length = n; // for the case n < 2
return l;
}
console.log(fibo(4));
Besides attempting to access l outside of the function, your l in the return statement is sometimes undefined when 0 or 1 is provided to the function. More importantly, l shouldn't be defined in the while loop but outside of it.
Moreover, you have variables such as i and j that are unused, and k which is being used but its scope is problematic:
It is outside of the function, so repeated runs will cause k to be continuously increased
k is used to track number of iterations, while your code indicates that n should be the quantity of numbers returned
Here is a way more simplified logic, assuming that n is supposed to the number of Fibonacci numbers to be returned:
If n === 1 or n === 2, you return the seed array
Otherwise you simply run a while loop that will only stop running once the seed array exceeds n. In the while loop, we retain the logic of pushing numbers, but otherwise there is no need to have any other variables being incremented.
See proof-of-concept example:
function fibbo(n) {
if (n === 1) {
return [0];
} else if (n === 2) {
return [0, 1];
} else {
const l = [0, 1];
while (n > l.length) {
const b = l.length;
l.push(l[b - 2] + l[b - 1]);
}
return l;
}
}
console.log(fibbo(4));
Using a while loop, my take will be as follows:
n = 0 || n = 1 . If n > 1 , it loops n - 1 times and within each iteration it adds the sum of previous two values in the existing sequence to the last index.
Once the loop finishes the whole sequence gets trimmed to the length of n and returned. (This last trimming piece was originally missing in my solution and added after reading trincot's answer)
const fibbo = (n) => {
const sequence = [0, 1];
let i = 2;
let next;
while(i <= n) {
next = sequence[i - 2] + sequence[i - 1];
sequence[i++] = next;
}
return sequence;
}
console.log(fibbo(6));

Palindrome Index Hackerrank with JavaScript

I submitted the following codes for the Palindrome Index challenge on HackerRank, but it fails some test cases with the error Exceeded time limit. I am not sure what is wrong with my codes.
Basically, I implement a distinct "helper" function to check if a string is a palindrome. In the PalindromeIndex function, I first check if a string is a palidrome then return -1, else I will loop through the string, try to remove each letter to see if the remaining string is a palindrome.
If it is, return the index right there; else, continue.
If the loop finishes without returning any index, that means there is no result, I return -1.
That is what I am trying to do, but I cannot find out the problem here. Thanks!
function isPalindrome(s) {
for (let i = 0; i < s.length; i++) {
if (s[i] !== s[s.length - 1 - i]) {
return false;
}
}
return true;
}
function palindromeIndex(s) {
let result = isPalindrome(s);
if (result) {
return -1;
}
else {
for (let i = 0; i < s.length; i++) {
let newS = s.slice(0,i) + s.slice(i+1);
if (isPalindrome(newS)) {
return i;
}
}
return -1
}
}
You need to optimize your palindrome function. Just run loop from 0 to half of array.
function isPalindrome(s) {
for (let i = 0; i < s.length/2; i++) {
if (s[i] !== s[s.length - 1 - i]) {
return false;
}
}
return true;
}
I will just draw up the basic idea, because I guess you want to try for yourself:
aaa = -1
aaab = 3
baaa = 0
bbaa = -1
You only want to loop through the string once.
You only need to loop through half the string, and do comparisons.
I would just focus on moving indexes.
String: baaa
Default variables:
position: -1
startNudge: 0
endNudge: 0
At index i (start at 0):
check i + startNudge against string.length - 1 - i + endNudge.
If not equal:
check index i + 1 + startNudge against string.length - 1 - i + endNudge.
if equal, set position to i and set startNudge to 1.
else, check i + startNudge against string.length - 2 - i + endNudge.
if equal, set position to string.length - 1 - i and set endNudge to -1.
else break loop by returning -1.
if position already saved, break loop by returning -1.
continue loop.
Return position if loop completes.
...else I will loop through the string, try to remove each letter to
see if the remaining string is a palindrome
This is the problem. It isn't necessary to loop through the whole string again, because as soon as a pair of letters is found in the string which break the palindrome - the answer must be either the index of the first letter or the second letter of the pair OR - if the removal of both letters don't produce a palindrome - then return -1.
function palindromeIndex(s) {
function isPalindrome(s) {
for(let i=0; i< Math.floor(s.length/2);i++) {
if(s[i] !== s[s.length-1-i]) {
return false;
}
}
return true;
}
for(let i=0; i< Math.floor(s.length/2); i++) {
if(s[i] !== s[s.length-1-i]) {
if(isPalindrome(s.slice(i, s.length-i-1))) {
return s.length-i-1;
} else if(isPalindrome(s.slice(i+1,s.length-i))) {
return i;
} else {
return -1;
}
}
}
return -1;
}
console.log(palindromeIndex('aabaa'))
console.log(palindromeIndex('aaab'))
console.log(palindromeIndex('baaa'))
console.log(palindromeIndex('baaabcc'))
This solution will have a time complexity of O(n) because only one loop is necessary

Why do I return undefined for a large number but not a small one? (Fibonacci Sequence Sum)

this code right here does work, but i don't quite understand why.
if i remove the interior return statement and keep only the one at the end, it will return the correct sum (44) for console.log(evenFibonnacisSum(100)), but undefined for console.log(evenFibonnacisSum(4000000)).
if i remove the exterior return statement and keep the one inside the if statement, it will return undefined for console.log(evenFibonnacisSum(100)) but the correct sum (46000000 or so) for console.log(evenFibonnacisSum(4000000)).
function evenFibonnacisSum(upperlimit){
let evenSum = 0
let seq = [0, 1]
for(i=0; i<=upperlimit; i++) {
if(evenSum<upperlimit) {
let next = seq[i]+seq[i+1];
seq.push(next)
if(seq[i]%2 === 0 && seq[i]<upperlimit) {
evenSum += seq[i]
}
} else {
return evenSum
}
}
return evenSum
}
console.log(evenFibonnacisSum(100))
console.log(evenFibonnacisSum(4000000))
it's not a huge problem, but it's really unintuitive. anyone have any insight into why this might be?
If you remove the interior return and pass in a large upperlimit, then it will attempt to execute the loop too many times. Your environment realizes that it is taking too long and aborts the function.
If you remove the external return then for 100 you never hit the interior return because the correct answer has evenSum < upperlimit.
I would suggest coding it like this instead:
function evenFibonnacisSum(upperlimit){
let evenSum = 0
let seq = [0, 1]
i = 0
while(seq[i] < upperlimit) {
let next = seq[i]+seq[i+1];
seq.push(next)
if(seq[i]%2 === 0) {
evenSum += seq[i]
}
i++
}
return evenSum
}

Check how many times a char appears in a string

Simply trying to find how many times a given character appears in a string but I can't solve it any other way then this simple for-loop. Is there a method that would solve this quicker or more eloquently other than using Regex?
function countCharacter(str, char) {
var count = 0;
for(var i = 0; i < str.length; i++){
if(str.charAt(i) === char)
count++;
}
return count;
}
There are many possible ways are available in the market.
I am adding a few of them.
Method 1:
str = "The man is as good as his word"
str.split('a')
output: (4) ["The m", "n is ", "s good ", "s his word"]
str.split('a').length - 1
output: 3
Method 2:
str = "The man is as good as his word"
str.split('').map( function(char,i)
{ if(char === 'a')
return i;
}
).filter(Boolean)
Output: (3) [5, 11, 19]
str.split('').map( function(char,i)
{ if(char === 'a')
return i;
}
).filter(Boolean).length
ouput: 3
Edit: As per comment we can also make use of filter().
str.split('').filter(function(char, i){
if(char == 'a'){
return i;
}
})
output: (3) ["a", "a", "a"]
str.split('').filter(function(char, i){
if(char == 'a'){
return i;
}
}).length
output: 3
----edited by adding more cases from answers----
there are several ways, you can use split/for/regex/reduce/indexOf like this:
function countCharacter_reduce(str, ch) {
return Array.prototype.reduce.call(str, (prev, cur) => cur === ch && ++prev && prev, 0);
}
function countCharacter_split(str, ch) {
return str.split(ch).length - 1;
}
function countCharacter_for(str, ch) {
for (var count = 0, ii = 0; ii < str.length; ii++) {
if (str[ii] === ch)
count++;
}
return count;
}
function countCharacter_regex(str, ch) {
return str.length - str.replace(new RegExp(ch, 'g'), '').length;
}
function countCharacter_indexOf(str, char) {
var start = 0;
var count = 0;
while ((start = str.indexOf(char, start) + 1) !== 0) {
count++;
}
return count;
}
performance of them by running 1,000,000 times on counting '/' in a string.
-- case1: running 1000000 times on ( 'this/is/a/path/with/extension', '/' )
countCharacter_reduce: 2389.880ms
countCharacter_regex: 496.251ms
countCharacter_split: 114.709ms
countCharacter_for: 152.012ms
countCharacter_indexOf: 90.330ms
-- case2: running 1000000 times on ( '////////////', '/' )
countCharacter_reduce: 1138.051ms
countCharacter_regex: 619.886ms
countCharacter_split: 121.945ms
countCharacter_for: 74.542ms
countCharacter_indexOf: 204.242ms
Conclusion ('>' means 'has better performance'):
for|split|indexOf > regex > reduce.
furthermore,
if the string contains more searching characters (like case2),
for>split>indexOf,
otherwise (like case1)
indexOf > split > for.
BTW: you can change the for indexOf method to fit multi-characters search (example is single character)
using reduce:
function countCharacter(str, char) {
return str.split('').reduce((a, x) => x === char ? ++a : a, 0);
}
I guess this involves regex which you wanted to avoid but it's pretty quick:
function countCharacter(str, char) {
return str.length - str.replace(new RegExp(char,"g"),"").length;
}
You can also try the str.split(char).length-1 approach suggested by Jaromanda.
Or, go all out with some fun recursion (pass 0 to startingFrom):
function countCharacter(str, char, startingFrom) {
var idx = str.indexOf(char, startingFrom);
return idx == -1 ? 0 : 1 + countCharacter(str, char, idx + 1);
}
You can get rid of the annoying extra argument at the cost of some efficiency:
function countCharacter(str, char) {
var idx = str.indexOf(char);
return idx == -1 ? 0 : 1 + countCharacter(str.substr(idx+1), char);
}
And here's a version optimized for speed (this is about 3 times faster on my browser than your original and much faster than the regex versions, according to jsperf):
function countCharacter(str, char) {
var start = 0;
var count = 0;
while((start = str.indexOf(char, start)+1) !== 0) {
count++;
}
return count;
}
Note that the indexOf approaches will generally be substantially faster than manually iterating through the string. See jsperf
Here you go. One line code
"hello".match(new RegExp('l','g')).length
replace 'l' with any char here, new RegExp('l','g').
that is
str.match(new RegExp(char,'g')).length
Have you thought of using the split() method? How about this -
function myFunction(str, char) {
return string.split(char).length - 1
}
Let me know what you think of this solution.

Detecting number patterns in an array

I'm trying to work through the following exercise. There is a problem with my code but I don't understand what it is...
Using the JavaScript language, have the function ArithGeo(arr) take the array of numbers stored in arr and return the string "Arithmetic" if the sequence follows an arithmetic pattern or return "Geometric" if it follows a geometric pattern. If the sequence doesn't follow either pattern return -1. An arithmetic sequence is one where the difference between each of the numbers is consistent, where as in a geometric sequence, each term after the first is multiplied by some constant or common ratio. Arithmetic example: [2, 4, 6, 8] and Geometric example: [2, 6, 18, 54]. Negative numbers may be entered as parameters, 0 will not be entered, and no array will contain all the same elements.
My code:
function ArithGeo(arr) {
if (for (i = 0; i< (arr.length - 2); i++) {
arr[i+1] - arr[i] == arr[i+2] - arr[i+1];
}){
return "Arithmetic";
} else if (for (i = 0; i< (arr.length - 2); i++) {
arr[i+1] / arr[i] == arr[i+2] / arr[i+1];
}){
return "Geometric";
} else {
return -1;
}
};
When I put an array like [5,10,15], I get "Unexpected token for". Any ideas?
Modified your code. Didn't change the logic, but the way it should be written.
function ArithGeo(arr) {
var ap, gp;
for (i = 0; i< (arr.length - 2); i++)
if(!(ap = arr[i+1] - arr[i] == arr[i+2] - arr[i+1])) break;
if(ap) return "Arithmetic";
for (i = 0; i< (arr.length - 2); i++)
if(!(gp = arr[i+1] / arr[i] == arr[i+2] / arr[i+1])) break;
if(gp) return "Geometric";
return -1;
};
It looks like you're trying to get the result of the for loop, but loops don't have return values. Consider defining a boolean variable (initially set to true) outside of the loop and AND it with the result of the comparison you perform inside the loop; the variable will be true at the end if each iteration of the loop ANDs true to it.
For example:
var test = true;
for(var i=0; i<5; i++) {
test = test && ( i != 6 );
}
if(test) {
alert("i was never 6");
}

Categories