I am looking on performance improvement of the script below. I am pretty sure it can be substantially modified, I did it as it was first thing that came to my head and it is only for demonstration purposes on what I am looking for.
function pad(nn, width, z) {
z = z || '0';
nn = nn + '';
return nn.length >= width ? nn : new Array(width - nn.length + 1).join(z) + nn;
}
var makeIntoBinary = function(ii, length) {
return pad(ii.toString(2), length);
}
var makeIntoTrueFalse = function(binary) {
var str = '';
for (iii = 0; iii < binary.length; iii++) {
if (binary[iii] == '0') {
str += ' false';
} else {
str += ' true';
}
};
console.log(str + ' ' + binary);
}
var runner = function(n) {
var iter = Math.pow(2, n);
for (i = 0; i < iter; i++) {
makeIntoTrueFalse(makeIntoBinary(i, n));
}
}
What I am looking for is to generate sets of words for all possible combinations which is essentially doing the binary above. (runner(2); would produce false false, false true, true false, true true) I am looking for lightning fast algorithm that gets me to this point.
Try manipulating bits directly, without extraneous string conversions.
function combinations(n) {
var r = [];
for(var i = 0; i < (1 << n); i++) {
var c = [];
for(var j = 0; j < n; j++) {
c.push(i & (1 << j) ? 'true' : 'false');
}
r.push(c.join(' '));
}
return r;
}
r = combinations(prompt('size?'));
document.write(JSON.stringify(r));
Just for the record, this is probably slower, but way nicer:
Number.prototype.times = function(fn) {
var r = [];
for(var i = 0; i < this; i++)
r.push(fn(i));
return r;
}
function combinations(n) {
return (1 << n).times(function(i) {
return n.times(function(j) {
return Boolean(i & 1 << j);
});
});
}
Related
I am having some trouble understanding a pattern I am seeing with a leet code problem I have been doing. The question regards detecting the longest palindrome in a string. I figured using a hashmap to determine palindromes, where length 1= palindrome, length 2 where characters are the same is a palindrome, and where if the first and last characters, and all the characters in the middle is a palindrome (detected by hash map) Basically this implementation with a hash map should be able to detect if something is a palindrome of 0(1) time complexity. But its much slower than the implementation without a hashmap, where we use a for loop to detect if a substring is a palindrome in 0(n): Here are the two imeplementations, does anyone know why the first is so much slower than the second. Shouldn't it be much quicker?
Slow implementation
/**
* #param {string} s
* #return {string}
*/
var longestPalindrome = function (s) {
let palindromes = new Map();
let palin = s[0];
for (let c = 0; c <= s.length; c++) {
for (let r = 0; r < c; r++) {
let tempStr = s.slice(r, c);
if (tempStr.length == 1) {
palin = tempStr.length > palin.length ? tempStr : palin;
palindromes.set(r + "r" + c + "c", true);
} else if (tempStr.length == 2) {
if (tempStr[0] === tempStr[1]) {
palin = tempStr.length > palin.length ? tempStr : palin;
palindromes.set(r + "r" + c + "c", true);
}
} else if (tempStr[0] === tempStr[tempStr.length - 1]) {
if (palindromes.get(r + 1 + "r" + (c - 1) + "c") === true) {
palin = tempStr.length > palin.length ? tempStr : palin;
palindromes.set(r + "r" + c + "c", true);
}
} else {
palindromes.set(r + "r" + c + "c", false);
}
}
}
return palin;
};
Faster implementation, for some reason
var isPalidrome = function (s) {
for (var i = 0; i < s.length / 2; i++) {
if (s[i] !== s[s.length - i - 1]) {
return false;
}
}
return true;
};
var longestPalindrome = function (s) {
let palindromes = new Map();
var max = s[0];
let palin = "";
for (let c = 0; c <= s.length; c++) {
for (let r = 0; r < c; r++) {
let tempStr = s.slice(r, c);
if (isPalidrome(tempStr)) {
max = tempStr.length > max.length ? tempStr : max;
}
}
}
return max;
};
Thanks!
EDIT:
So by allocating and using array indices instead of the map, I made it finally run quicker than the N loop implementation. I guess my question is, why isn't the Map accesses 0(1). Is Javascript not using the most efficient map implementation or something? Do maps get very slow where their big for some reason? Some help understanding this would be appreciated.
So to Refactor my question: Why is this:
/**
* #param {string} s
* #return {string}
*/
var longestPalindrome = function (s) {
let palindromes2 = [];
for (let i = 0; i < s.length; i++) {
palindromes2.push([]);
}
let palin = s[0];
for (let c = 0; c <= s.length; c++) {
for (let r = 0; r < c; r++) {
let tempStr = s.slice(r, c);
if (tempStr.length == 1) {
palin = tempStr.length > palin.length ? tempStr : palin;
palindromes2[r][c] = true;
} else if (tempStr.length == 2) {
if (tempStr[0] === tempStr[1]) {
palin = tempStr.length > palin.length ? tempStr : palin;
palindromes2[r][c] = true;
} else {
palindromes2[r][c] = false;
}
} else if (tempStr[0] === tempStr[tempStr.length - 1]) {
if (palindromes2[r + 1][c - 1] === true) {
palin = tempStr.length > palin.length ? tempStr : palin;
palindromes2[r][c] = true;
} else {
palindromes2[r][c] = false;
}
} else {
palindromes2[r][c] = false;
}
}
}
return palin;
};
SOOOO much faster (6x+ faster) than the first implementation I posted, with a hash map.
Thanks.
I want reverse the string with the same order. We should not use the Array functions like split(), .reverse() and .join(). but we can use array.length.
Here i am attached my code. How to achieve that thing more efficiently.
var str = "i am javascript";
// result "i ma tpircsavaj"
function reverseString(myStr){
var strlen = myStr.length, result = "", reverseStr = "", reverseStrArr = [];
for(var i = strlen-1; i >= 0; i--){
reverseStr += myStr[i];
}
for(var j = 0; j < strlen; j++){
if(reverseStr[j] == " "){
reverseStrArr.push(result);
result = "";
}else{
result += reverseStr[j];
if(j + 1 == strlen){
reverseStrArr.push(result);
result = "";
}
}
}
for(var k=reverseStrArr.length - 1; k >= 0; k--){
result += reverseStrArr[k] + " "
}
console.log(result);
}
reverseString(str);
Use 2 pointers: i to indicate the current position and j to indicate the start index of the current word. Add the reversed of current word char by char when space.
Don't be fooled by the nested loops, the complexity is the same as yours: O(n) and somehow cleaner for me.
var string = "i love javascript and the whole world!"
var result = ""
var i = j = 0
var l = string.length
while (i++ < l) {
var k = i
if (string[i] === " " || (i === l - 1) && k++) {
while (--k >= j) result += string[k]
j = i + 1
result += " "
}
}
result = result && result.slice(0, -1) || ""
console.log(result)
You could take a loop, collect the characters of a word and reverse the word.
function reverse(string) {
var reversed = '';
while (reversed.length !== string.length) {
reversed = string[reversed.length] + reversed;
}
return reversed;
}
var string = "i am javascript",
temp = '',
result = '',
i = 0;
while (i < string.length) {
if (string[i] === ' ') {
result += (result && ' ') + reverse(temp);
temp = '';
} else {
temp += string[i];
}
i++;
}
if (temp) result += (result && ' ') + reverse(temp);
console.log(result);
An approach from the end.
function reverse(string) {
var reversed = '';
while (reversed.length !== string.length) {
reversed = string[reversed.length] + reversed;
}
return reversed;
}
var string = "i am javascript",
temp = '',
result = '',
i = string.length;
while (i--) {
if (string[i] === ' ') {
result = ' ' + reverse(temp) + result;
temp = '';
} else {
temp = string[i] + temp;
}
}
if (temp) result = reverse(temp) + result;
console.log(result);
function reverseStr(str) {
var ret = "";
for (var i = 0; i < str.length; i++) {
ret = str[i] + ret;
}
return ret;
}
function doIt(str) {
var ret = "", cur = "";
for (var i = 0; i < str.length; i++) {
var c = str.charAt(i);
if (c == ' ' || c == '.') {
ret += reverseStr(cur) + c;
cur = "";
} else {
cur += c;
}
}
ret += reverseStr(cur);
return ret;
}
console.log(doIt('Reverse the word in a string with the same order in javascript without using the array functions except .length'));
function reverse(word) {
if (word.length > 1) {
var newWord = '';
for (j = word.length-1; j >= 0; j--) {
newWord += word[j];
}
return newWord + ' ';
} else {
return word + ' ';
}
}
var text = "i am javascript";
var words = text.split(' ');
var result = '';
for (i = 0; i < words.length; i++) {
result += reverse(words[i]);
}
console.log(result);
I am working on this kata https://www.codewars.com/kata/count-9-s-from-1-to-n/train/javascript
and i have written this code for it, but its not working. This question is similar to this one Count the number of occurrences of 0's in integers from 1 to N
but it is different because searching for 9's is practically very different to searching for 0's.
think part of the problem with this code is that it takes too long to run...
any advice appreciated!
function has9(n) {
var nine = [];
var ninearr = n.toString().split('');
for (var j = 0; j < ninearr.length; j++) {
if (ninearr[j] == '9') {
nine.push(ninearr[j]);
}
}
return nine.length;
}
function number9(n) {
var arr = [];
var arrnew = [];
for (var i = 0; i <= n; i++) {
arr.push(i);
}
for (var l = 0; l < arr.length; l++) {
arrnew.push(has9(l));
}
var sum = arrnew.reduce((a, b) => a + b, 0);
return sum;
}
Why not a regex based solution? (Too slow as well?)
const count9s = num => num.toString().match(/9/g).length
console.log(count9s(19716541879)) // 2
console.log(count9s(919191919191919)) // 8
console.log(count9s(999)) // 3
console.log(count9s(999999)) // 6
I have taken the above hint and completely re written the code, which I now feel should work, and it does for most inputs, but codewars is saying it fails on some of them. any ideas why?
function nines(n){
if(n>=100){
var q= Math.floor(n/100);
var nq= q * 20;
var r = (n%100);
var s = Math.floor(r/9);
if (r<=90){
return s + nq;
}
if (r == 99){
return 20 + nq;
}
if (90 < r < 100 && r!= 99){
var t = (r-90);
return nq + s + t;
}
}
if (n<100){
if (n<=90){
var a = Math.floor(n/9);
return a ;
}
if (n == 99){
return 20
}
if (90 < n < 100 && n!= 99){
var c = (n-90);
return 10 + c;
}
}
}
=== UPDATE ===
I just solved your kata using
function number9Helper(num) {
var pow = Math.floor(Math.log10(num));
var round = Math.pow(10, pow);
var times = Math.floor(num / round);
var rest = Math.abs(num - (round * times));
var res = pow * (round==10 ? 1 : round / 10) * times;
if (num.toString()[0] == '9') res += rest;
if (rest < 9) return res;
else return res + number9Helper(rest);
}
function number9(num) {
var res = number9Helper(num);
res = res + (num.toString().split('9').length-1);
return res;
}
== Function below works but is slow ===
So, could something like this work for you:
for (var nines=0, i=1; i<=n; i++) nines += i.toString().split('9').length-1;
Basically, there are many way to achieve what you need, in the end it all depends how do you want to approach it.
You can test it with
function nines(n) {
for (var nines=0, i=1; i<=n; i++) nines += i.toString().split('9').length-1;
return nines;
}
function number9(n) {
if (n < 8) {
return 0
};
if (n === 9) {
return 1
};
if (n > 10) {
let str = ''
for (let i = 9; i <= n; i++) {
str += String(i)
}
return str.match(/[9]/g).length
}
}
I wrote the following function to find the longest palindrome in a string. It works fine but it won't work for words like "noon" or "redder". I fiddled around and changed the first line in the for loop from:
var oddPal = centeredPalindrome(i, i);
to
var oddPal = centeredPalindrome(i-1, i);
and now it works, but I'm not clear on why. My intuition is that if you are checking an odd-length palindrome it will have one extra character in the beginning (I whiteboarded it out and that's the conclusion I came to). Am I on the right track with my reasoning?
var longestPalindrome = function(string) {
var length = string.length;
var result = "";
var centeredPalindrome = function(left, right) {
while (left >= 0 && right < length && string[left] === string[right]) {
//expand in each direction.
left--;
right++;
}
return string.slice(left + 1, right);
};
for (var i = 0; i < length - 1; i++) {
var oddPal = centeredPalindrome(i, i);
var evenPal = centeredPalindrome(i, i);
if (oddPal.length > result.length)
result = oddPal;
if (evenPal.length > result.length)
result = evenPal;
}
return "the palindrome is: " + result + " and its length is: " + result.length;
};
UPDATE:
After Paul's awesome answer, I think it makes sense to change both variables for clarity:
var oddPal = centeredPalindrome(i-1, i + 1);
var evenPal = centeredPalindrome(i, i+1);
You have it backwards - if you output the "odd" palindromes (with your fix) you'll find they're actually even-length.
Imagine "noon", starting at the first "o" (left and right). That matches, then you move them both - now you're comparing the first "n" to the second "o". No good. But with the fix, you start out comparing both "o"s, and then move to both "n"s.
Example (with the var oddPal = centeredPalindrome(i-1, i); fix):
var longestPalindrome = function(string) {
var length = string.length;
var result = "";
var centeredPalindrome = function(left, right) {
while (left >= 0 && right < length && string[left] === string[right]) {
//expand in each direction.
left--;
right++;
}
return string.slice(left + 1, right);
};
for (var i = 0; i < length - 1; i++) {
var oddPal = centeredPalindrome(i, i + 1);
var evenPal = centeredPalindrome(i, i);
if (oddPal.length > 1)
console.log("oddPal: " + oddPal);
if (evenPal.length > 1)
console.log("evenPal: " + evenPal);
if (oddPal.length > result.length)
result = oddPal;
if (evenPal.length > result.length)
result = evenPal;
}
return "the palindrome is: " + result + " and its length is: " + result.length;
};
console.log(
longestPalindrome("nan noon is redder")
);
This will be optimal if the largest palindrome is found earlier.
Once its found it will exit both loops.
function isPalindrome(s) {
//var rev = s.replace(/\s/g,"").split('').reverse().join(''); //to remove space
var rev = s.split('').reverse().join('');
return s == rev;
}
function longestPalind(s) {
var maxp_length = 0,
maxp = '';
for (var i = 0; i < s.length; i++) {
var subs = s.substr(i, s.length);
if (subs.length <= maxp_length) break; //Stop Loop for smaller strings
for (var j = subs.length; j >= 0; j--) {
var sub_subs = subs.substr(0, j);
if (sub_subs.length <= maxp_length) break; // Stop loop for smaller strings
if (isPalindrome(sub_subs)) {
maxp_length = sub_subs.length;
maxp = sub_subs;
}
}
}
return maxp;
}
Here is another take on the subject.
Checks to make sure the string provided is not a palindrome. If it is then we are done. ( Best Case )
Worst case 0(n^2)
link to
gist
Use of dynamic programming. Break each problem out into its own method, then take the solutions of each problem and add them together to get the answer.
class Palindrome {
constructor(chars){
this.palindrome = chars;
this.table = new Object();
this.longestPalindrome = null;
this.longestPalindromeLength = 0;
if(!this.isTheStringAPalindrome()){
this.initialSetupOfTableStructure();
}
}
isTheStringAPalindrome(){
const reverse = [...this.palindrome].reverse().join('');
if(this.palindrome === reverse){
this.longestPalindrome = this.palindrome;
this.longestPalindromeLength = this.palindrome.length;
console.log('pal is longest', );
return true;
}
}
initialSetupOfTableStructure(){
for(let i = 0; i < this.palindrome.length; i++){
for(let k = 0; k < this.palindrome.length; k++){
this.table[`${i},${k}`] = false;
}
}
this.setIndividualsAsPalindromes();
}
setIndividualsAsPalindromes(){
for(let i = 0; i < this.palindrome.length; i++){
this.table[`${i},${i}`] = true;
}
this.setDoubleLettersPlaindrome();
}
setDoubleLettersPlaindrome(){
for(let i = 0; i < this.palindrome.length; i++){
const firstSubstring = this.palindrome.substring(i, i + 1);
const secondSubstring = this.palindrome.substring(i+1, i + 2);
if(firstSubstring === secondSubstring){
this.table[`${i},${i + 1}`] = true;
if(this.longestPalindromeLength < 2){
this.longestPalindrome = firstSubstring + secondSubstring;
this.longestPalindromeLength = 2;
}
}
}
this.setAnyPalindromLengthGreaterThan2();
}
setAnyPalindromLengthGreaterThan2(){
for(let k = 3; k <= this.palindrome.length; k++){
for(let i = 0; i <= this.palindrome.length - k; i++){
const j = i + k - 1;
const tableAtIJ = this.table[`${i+1},${j-1}`];
const stringToCompare = this.palindrome.substring(i, j +1);
const firstLetterInstringToCompare = stringToCompare[0];
const lastLetterInstringToCompare = [...stringToCompare].reverse()[0];
if(tableAtIJ && firstLetterInstringToCompare === lastLetterInstringToCompare){
this.table[`${i},${j}`] = true;
if(this.longestPalindromeLength < stringToCompare.length){
this.longestPalindrome = stringToCompare;
this.longestPalindromeLength = stringToCompare.length;
}
}
}
}
}
printLongestPalindrome(){
console.log('Logest Palindrome', this.longestPalindrome);
console.log('from /n', this.palindrome );
}
toString(){
console.log('palindrome', this.palindrome);
console.log(this.table)
}
}
// const palindrome = new Palindrome('lollolkidding');
// const palindrome = new Palindrome('acbaabca');
const palindrome = new Palindrome('acbaabad');
palindrome.printLongestPalindrome();
//palindrome.toString();
function longestPalindrome(str){
var arr = str.split("");
var endArr = [];
for(var i = 0; i < arr.length; i++){
var temp = "";
temp = arr[i];
for(var j = i + 1; j < arr.length; j++){
temp += arr[j];
if(temp.length > 2 && temp === temp.split("").reverse().join("")){
endArr.push(temp);
}
}
}
var count = 0;
var longestPalindrome = "";
for(var i = 0; i < endArr.length; i++){
if(count >= endArr[i].length){
longestPalindrome = endArr[i-1];
}
else{
count = endArr[i].length;
}
}
console.log(endArr);
console.log(longestPalindrome);
return longestPalindrome;
}
longestPalindrome("abracadabra"));
let str = "HYTBCABADEFGHABCDEDCBAGHTFYW12345678987654321ZWETYGDE";
let rev = str.split("").reverse().join("").trim();
let len = str.length;
let a="";
let result = [];
for(let i = 0 ; i < len ; i++){
for(let j = len ; j > i ; j--){
a = rev.slice(i,j);
if(str.includes(a)){
result.push(a);
break;
}
}
}
result.sort((a,b) => { return b.length - a.length})
let logPol = result.find((value)=>{
return value === value.split('').reverse().join('') && value.length > 1
})
console.log(logPol);
function longest_palindrome(s) {
if (s === "") {
return "";
}
let arr = [];
let _s = s.split("");
for (let i = 0; i < _s.length; i++) {
for (let j = 0; j < _s.length; j++) {
let word = _s.slice(0, j + 1).join("");
let rev_word = _s
.slice(0, j + 1)
.reverse()
.join("");
if (word === rev_word) {
arr.push(word);
}
}
_s.splice(0, 1);
}
let _arr = arr.sort((a, b) => a.length - b.length);
for (let i = 0; i < _arr.length; i++) {
if (_arr[arr.length - 1].length === _arr[i].length) {
return _arr[i];
}
}
}
longest_palindrome('bbaaacc')
//This code will give you the first longest palindrome substring into the string
var longestPalindrome = function(string) {
var length = string.length;
var result = "";
var centeredPalindrome = function(left, right) {
while (left >= 0 && right < length && string[left] === string[right]) {
//expand in each direction.
left--;
right++;
}
return string.slice(left + 1, right);
};
for (var i = 0; i < length - 1; i++) {
var oddPal = centeredPalindrome(i, i + 1);
var evenPal = centeredPalindrome(i, i);
if (oddPal.length > 1)
console.log("oddPal: " + oddPal);
if (evenPal.length > 1)
console.log("evenPal: " + evenPal);
if (oddPal.length > result.length)
result = oddPal;
if (evenPal.length > result.length)
result = evenPal;
}
return "the palindrome is: " + result + " and its length is: " + result.length;
};
console.log(longestPalindrome("n"));
This will give wrong output so this condition need to be taken care where there is only one character.
public string LongestPalindrome(string s) {
return LongestPalindromeSol(s, 0, s.Length-1);
}
public static string LongestPalindromeSol(string s1, int start, int end)
{
if (start > end)
{
return string.Empty;
}
if (start == end)
{
char ch = s1[start];
string s = string.Empty;
var res = s.Insert(0, ch.ToString());
return res;
}
if (s1[start] == s1[end])
{
char ch = s1[start];
var res = LongestPalindromeSol(s1, start + 1, end - 1);
res = res.Insert(0, ch.ToString());
res = res.Insert(res.Length, ch.ToString());
return res;
}
else
{
var str1 = LongestPalindromeSol(s1, start, end - 1);
var str2 = LongestPalindromeSol(s1, start, end - 1);
if (str1.Length > str2.Length)
{
return str1;
}
else
{
return str2;
}
}
}
This is in JS ES6. much simpler and works for almost all words .. Ive tried radar, redder, noon etc.
const findPalindrome = (input) => {
let temp = input.split('')
let rev = temp.reverse().join('')
if(input == rev){
console.log('Palindrome', input.length)
}
}
//i/p : redder
// "Palindrome" 6
I looked it up and this pattern is Hofstadter Female sequence. The equations are:
M(n) = n-F(M(n-1))
F(n) = n-M(F(n-1))
but I'm not sure how to put that into code.
So far I have:
while () {
_p++
_r++
if (_p % 2 === 0) {
_r = _p - 1;
}
}
Any help?
Without memoization:
function F(n)
{
return 0 < n ? n - M(F(n-1)) : 1
}
function M(n)
{
return 0 < n ? n - F(M(n-1)) : 0
}
var N = 10;
var f = [];
var m = [];
for (var i = 0; i <= N; ++i) {
f.push(F(i));
m.push(M(i));
}
console.log('F: ' + f.join(','))
console.log('M: ' + m.join(','))
Output:
F: 1,1,2,2,3,3,4,5,5,6,6
M: 0,0,1,2,2,3,4,4,5,6,6
http://jsfiddle.net/KtGBg/1/
Recursion should be avoided, if possible, so you can cache the already-calculated values for F(n) and M(n) :
var f = new Array();
var m = new Array();
function F(n){
if(f[n] != undefined) {
return f[n];
}
if (n==0) {
value = 1;
} else {
value = n - M(F(n-1));
}
f[n] = value;
return value;
}
function M(n){
if(m[n] != undefined) {
return m[n];
}
if (n==0) {
value = 0;
} else {
value = n - F(M(n-1));
}
m[n] = value;
return value;
}
This yields a much faster result for greater numbers (try it with 10000)
how about:
function F(n){
if (n==0) return 1
else return n - M(F(n-1))
}
function M(n){
if (n==0) return 0
else return n - F(M(n-1))
}
var str = ""
for(var i=0; i<=10; i++) str += F(i) + ", "
console.log(str.substr(0,str.length-2))
Similar to GaborSch's answer, you could use Doug Crockford's memoizer function, which can be found in Chapter 4 of Javascript: The Good Parts. Using memoization took the calculation time for the first 150 terms of the male and female Hofstadter sequences down to 256 ms as compared to almost 8 seconds without memoization.
var memoizer = function (memo, formula) {
var recur = function (n) {
var result = memo[n];
if (typeof result !== 'number') {
result = formula(recur, n);
memo[n] = result;
}
return result;
};
return recur;
};
var maleHofstadter = memoizer([0], function (recur, n) {
return n - femaleHofstadter(recur(n-1));
});
var femaleHofstadter = memoizer([1], function (recur, n) {
return n - maleHofstadter(recur(n-1));
});
var N = 150;
var f = [];
var m = [];
for (var i = 0; i <= N; ++i) {
f.push(femaleHofstadter(i));
m.push(maleHofstadter(i));
}
console.log('F: ' + f.join(','));
console.log('M: ' + m.join(','));