Levenshtein distance from index 0 - javascript

I've been working through "The Algorithm Design Manual" section 8.2.1 Edit Distance by Recursion. In this section Skiena writes, "We can define a recursive algorithm using the observation that the last character in the string must either be matched, substituted, inserted, or deleted." That got me wondering, why the last character? This is true for any character based on the problem definition alone. The actual Levenshtein distance algorithm makes recursive calls from the back of the strings. Why? There's no reason you couldn't do the opposite, right? Is it just a simpler, more elegant syntax?
I'm flipping the algorithm around, so it iterates from the front of the string. My attempt is below. I know my implementation doesn't work completely (ex: minDistance("industry", "interest") returns 5 instead of 6). I've spent a couple hours trying to figure out what I'm doing wrong, but I'm not seeing it. Any help would be much appreciated.
var matchChar = (c,d) => c === d ? 0 : 1;
var minDistance = function(word1, word2) {
var stringCompare = function(s, t, i, j) {
if(i === s.length) return Math.max(t.length-s.length-1,0)
if(j === t.length) return Math.max(s.length-t.length-1,0)
if(cache[i][j] !== undefined) {
return cache[i][j]
}
let match = stringCompare(s,t,i+1,j+1) + matchChar(s[i], t[j]);
let insert = stringCompare(s,t,i,j+1) + 1;
let del = stringCompare(s,t,i+1,j) + 1;
let lowestCost = Math.min(match, insert, del)
cache[i][j] = lowestCost
return lowestCost
};
let s = word1.split('')
s.push(' ')
s = s.join('')
let t = word2.split('')
t.push(' ')
t = t.join('')
var cache = []
for(let i = 0; i < s.length; i++) {
cache.push([])
for(let j = 0; j < t.length; j++) {
cache[i].push(undefined)
}
}
return stringCompare(s, t, 0, 0)
}

The lines
if(i === s.length) return Math.max(t.length-s.length-1,0)
if(j === t.length) return Math.max(s.length-t.length-1,0)
look wrong to me. I think they should be
if(i === s.length) return t.length-j
if(j === t.length) return s.length-i

Related

How to limit a number between several numbers (get the most nearest small number)? [duplicate]

Example: I have an array like this: [0,22,56,74,89] and I want to find the closest number downward to a different number. Let's say that the number is 72, and in this case, the closest number down in the array is 56, so we return that. If the number is 100, then it's bigger than the biggest number in the array, so we return the biggest number. If the number is 22, then it's an exact match, just return that. The given number can never go under 0, and the array is always sorted.
I did see this question but it returns the closest number to whichever is closer either upward or downward. I must have the closest one downward returned, no matter what.
How do I start? What logic should I use?
Preferably without too much looping, since my code is run every second, and it's CPU intensive enough already.
You can use a binary search for that value. Adapted from this answer:
function index(arr, compare) { // binary search, with custom compare function
var l = 0,
r = arr.length - 1;
while (l <= r) {
var m = l + ((r - l) >> 1);
var comp = compare(arr[m]);
if (comp < 0) // arr[m] comes before the element
l = m + 1;
else if (comp > 0) // arr[m] comes after the element
r = m - 1;
else // arr[m] equals the element
return m;
}
return l-1; // return the index of the next left item
// usually you would just return -1 in case nothing is found
}
var arr = [0,22,56,74,89];
var i=index(arr, function(x){return x-72;}); // compare against 72
console.log(arr[i]);
Btw: Here is a quick performance test (adapting the one from #Simon) which clearly shows the advantages of binary search.
var theArray = [0,22,56,74,89];
var goal = 56;
var closest = null;
$.each(theArray, function(){
if (this <= goal && (closest == null || (goal - this) < (goal - closest))) {
closest = this;
}
});
alert(closest);
jsFiddle http://jsfiddle.net/UCUJY/1/
Array.prototype.getClosestDown = function(find) {
function getMedian(low, high) {
return (low + ((high - low) >> 1));
}
var low = 0, high = this.length - 1, i;
while (low <= high) {
i = getMedian(low,high);
if (this[i] == find) {
return this[i];
}
if (this[i] > find) {
high = i - 1;
}
else {
low = i + 1;
}
}
return this[Math.max(0, low-1)];
}
alert([0,22,56,74,89].getClosestDown(75));
Here's a solution without jQuery for more effiency. Works if the array is always sorted, which can easily be covered anyway:
var test = 72,
arr = [0,56,22,89,74].sort(); // just sort it generally if not sure about input, not really time consuming
function getClosestDown(test, arr) {
var num = result = 0;
for(var i = 0; i < arr.length; i++) {
num = arr[i];
if(num <= test) { result = num; }
}
return result;
}
Logic: Start from the smallest number and just set result as long as the current number is smaller than or equal the testing unit.
Note: Just made a little performance test out of curiosity :). Trimmed my code down to the essential part without declaring a function.
Here's an ES6 version using reduce, which OP references. Inspired by this answer get closest number out of array
lookup array is always sorted so this works.
const nearestBelow = (input, lookup) => lookup.reduce((prev, curr) => input >= curr ? curr : prev);
const counts = [0,22,56,74,89];
const goal = 72;
nearestBelow(goal, counts); // result is 56.
Not as fast as binary search (by a long way) but better than both loop and jQuery grep https://jsperf.com/test-a-closest-number-function/7
As we know the array is sorted, I'd push everything that asserts as less than our given value into a temporary array then return a pop of that.
var getClosest = function (num, array) {
var temp = [],
count = 0,
length = a.length;
for (count; count < length; count += 1) {
if (a[count] <= num) {
temp.push(a[count]);
} else {
break;
}
}
return temp.pop();
}
getClosest(23, [0,22,56,74,89]);
Here is edited from #Simon.
it compare closest number before and after it.
var test = 24,
arr = [76,56,22,89,74].sort(); // just sort it generally if not sure about input, not really time consuming
function getClosest(test, arr) {
var num = result = 0;
var flag = 0;
for(var i = 0; i < arr.length; i++) {
num = arr[i];
if(num < test) {
result = num;
flag = 1;
}else if (num == test) {
result = num;
break;
}else if (flag == 1) {
if ((num - test) < (Math.abs(arr[i-1] - test))){
result = num;
}
break;
}else{
break;
}
}
return result;
}

i need to find the distance between two characters in string js. i have a solution but i can't understand the snippet of code related to if statement

The task was to write a function subLength() that takes 2 parameters, a string and a single character. The function should search the string for the two occurrences of the character and return the length between them including the 2 characters. If there are less than 2 or more than 2 occurrences of the character the function should return 0.
const subLength = (str, char) => {
let charCount = 0;
let len = -1;
for (let i=0; i<str.length; i++) {
if (str[i] == char) {
charCount++;
if (charCount > 2) {
return 0;
}
// could somebody explain why -1 is equal to len and then len is reassigned to i???
if (len == -1) {
len = i;
} else {
len = i - len + 1
}
}
}
if (charCount < 2) {
return 0;
}
return len;
};
dude there are other ways to do this.
see if my code helps you to understand any better :)
const subLength = (str, char) => {
let charCount = 0;
let letterFound = [];
let arr = str.split('');
for (let i = 0; i < arr.length; i++) {
if (arr[i] === char) {
letterFound.push(i);
charCount++;
}
}
let result = arr.slice(letterFound[0], letterFound[1]).length + 1;
if (charCount > 2 || charCount < 2) return 0;
return result;
}
console.log(subLength('saturday', 'a'));
in the first occurrence len= -1 so:
(len ==-1) turned to true;
and len is changed to i
so len=i;
in the second occurance len is not -1 so:
len = i - len -1;
in essence, in the above expression, len keeps the index of the first occurrence, and i has the index of second occurrence, so the difference will be, the difference between two occurrences, 'qweraq' : first occurrance: 0, second: 6. 6-0-1= 5 is difference;
This is how I manage to rewrite your code.
Hope this helps.
const subLength = (str, char) => {
const arr = str.split('');
let count = 0;
arr.forEach(letter => letter === char ? count++ : '')
const result = arr.some(letter => {
if (arr.indexOf(char) !== arr.lastIndexOf(char) && count == 2) {
return true
}
return false
})
return result ? Math.abs(arr.indexOf(char) - arr.lastIndexOf(char)) + 1 : 0
}
Whilst its possible to calculate the indexes and the count within a single loop, given the triviality I'd favor standard API methods for scanning. one could always optimize later if the application actually called for it.
First we convert the string to an array so that we have the ability to filter for matches to the input character, and derive the length.
Only if there are 2 occurrences do we calculate the distance between using the indexOf and lastIndexOf methods on string (note that these will only require a second full scan of the string if the two occurrences are consecutive).
Otherwise, the result should be 0.
const subLength = (str, chr) => {
const count = str.split('').filter(ltr => ltr === chr).length
if (count === 2) {
return (str.lastIndexOf(chr) - str.indexOf(chr)) + 1
}
return 0
}
console.log(subLength('asddfghvcba', 'a'))
console.log(subLength('asddfghvcba', 'x'))
console.log(subLength('aaaaaaaaaaa', 'a'))

JavaScript bug: function returns NaN even though typeof function returns "number"

I want to find the largest number resulted from the permutations of a given number.
If the given number is 123, the largest number resulted from the permutations of it is 321.
EDIT
I have done some progress by changing
if (str.length === 0) return "";
if (str.length === 1) return str;
to
if (str.length === 0) {
return "";
} else {
return str;
}
A problem remains, however: the function returns a string. In fact, the array members are strings instead of numbers.
function findPerms(num) {
var str = num.toString();
if (str.length === 0) {
return "";
} else {
return str;
}
let result = [];
for (let i = 0; i < str.length; i++) {
const currentChar = str[i];
const remainingChars = str.slice(0, i) + str.slice(i + 1);
for (let j = 0; j < remainingChars.length; j++) {
result.push(Number(currentChar + findPerms(remainingChars)[j]));
}
}
result.sort(function(a, b) {
return a - b;
});
return result[result.length - 1];
}
console.log(findPerms(11121));
console.log(typeof findPerms(11121));
For this purpose I did:
function findPerms(num) {
var str = num.toString();
if (str.length === 0) return "";
if (str.length === 1) return str;
let result = [];
for (let i = 0; i < str.length; i++) {
const currentChar = str[i];
const remainingChars = str.slice(0, i) + str.slice(i + 1);
for (let j = 0; j < remainingChars.length; j++) {
result.push(Number(currentChar + findPerms(remainingChars)[j]));
}
}
result.sort(function(a, b) {
return a - b;
});
return result[result.length - 1];
}
console.log(findPerms(11121));
console.log(typeof findPerms(11121));
The problem
I must have made a mistake I was unable to spot because the function above returns NaN (even though typeof findPerms(11121) returns "number").
Where is my mistake?
There are a couple of issues with your code, but let's try to define the requirements correctly first.
If I understand what you want to do is to find all permutations of a number and select the highest possible permutation of the digits in the number.
What you are trying to do is something like this:
Create an empty list
Loop through the digits of the initial number.
For each digit loop trough the remaining digits and find all possible combinations left and add them to the initial empty list.
After you have all possible combinations of numbers in the list sort them and return the highest result.
There are 2 problems here, first there are some mistakes in the execution of the code and your algorithm is not very efficient.
Let's tackle the first one:
When you create the result list you define it inside the recursive function, this means that it will be overwritten with an empty array on each recursive call. Usually when you apply this technique you either define the result array outside of the recursive function or you pass it as a argument in order to be able to iterate over the result.
You seem to not have a good grasp of how function scope and how closures work in JS. I would advise reading some resources on MDN for this.
Another problem here is that you expect the function to receive a number as argument, but when you call it in
result.push(Number(currentChar + findPerms(remainingChars)
remainingChars is a string, hence the NaN result.
Now on to the second problem. Considering that you want the highest possible number from a series of digits you could just sort the digits from highest to smallest and just provide the result.
EG:
If the number is 18, you can sort it in array like [8, 1] and then concatenate it to 81.
The code would probably look smth like:
function findHighestPerm(no) {
const arr = no.toString().split('');
return parseInt(arr.sort((a, b) => b - a).join(''));
}
PS: I am aware that my code is not the most efficient or elegant, but I am just lazy and it works ;)
Edit
I think this is what you would like to do.
Make an array of all digit permutations of a number using recursion.
Sort the array.
Return the permutation with the largest number.
This can be done by breaking the problem it into two functions.
The first function returns an array of all permutations of a number.
The second function takes an array result from the first function then sorts it. Then it returns the last value.
Your recursion was not operating properly. That is why it didn't work.
<html><head><title>6770990</title>
<script>
function findPerms(num) {
var str = num.toString(); var result = [];
if (str.length == 0) return result;
if (str.length == 1) return [parseInt(str)];
for (var i = 0; i < str.length; i++) {
const currentChar = str[i];
const remainingChars = str.slice(0, i) + str.slice(i + 1)
var perms = findPerms(remainingChars) //----Permutation array of remaining chars.
for (var j = 0; j < perms.length; j++) {
result.push(parseInt("" + currentChar + perms[j]));
}
}
return result; //----All permutations for num.
}
function findLargestPerm(num)
{ var perms = findPerms(num); perms.sort(function(a,b) {return a-b;});
return perms[perms.length-1];
}
</script>
</head></body>
<script>
var r = findLargestPerm(11121); alert("r=" + r);
</script>
</body></html>
Previous answer
It would be more efficient to just sort the digits in reverse order and concatenate into a number. This would be the largest permutation.
The function findPerms() is trying to find the largest permutation of a set of digits. Sometimes it helps to rename a function to what it does, so that it is easier to follow the logic.
The line that calls the function using recursion again only needs to be called once for each current character.
The following code shows the steps that happen.
<html><head><title>6770990</title>
<script>
var msg="";
function findLargestPermutation(num)
{ var str = num.toString(); msg += "findLargestPermutation(" + str + ")<br>";
if (str.length === 0) return "";
if (str.length === 1) return str;
let result = [];
for (let i = 0; i < str.length; i++)
{ const currentChar = str[i];
const remainingChars = str.slice(0, i) + str.slice(i + 1);
msg += "currentChar=" + currentChar + " remainingChars=" + remainingChars + "<br>";
//----The j loop should be removed and just use this line.
var num = Number(currentChar + findLargestPermutation(remainingChars));
msg += "num=" + num + "<br>";
result.push(num);
}
result.sort(function(a, b) {return a - b;});
msg += "findLargestPermutation()=" + result[result.length - 1] + "<br>";
return result[result.length - 1];
}
</script>
</head><body>
<div id="div"></div>
<script>
var lp = findLargestPermutation(11121);
alert("lp=" + lp + " typeof lp=" + typeof lp);
document.getElementById("div").innerHTML = msg;
</script>
</body></html>

Rearranging a string to be a palindrome

I'm trying to solve the problem of: Given an array of strings with only lower case letters, make a function that returns an array of those same strings, but each string has its letters rearranged such that it becomes a palindrome (if not possible then return -1). I'm a bit stuck on how I should be rearranging the letters.
let arr = ["hello", "racecra"];
I created a function to first check if a word is a palindrome :
function isPalindrome(arr) {
let obj = {};
for (var x = 0; x < str.length; x++) {
if (obj[arr[x]]) {
obj[arr[x]] += 1;
} else {
obj[arr[x]] = 1;
}
}
let countOdd = 0;
let countEven = 0;
for (let x of Object.values(obj)) {
if (x % 2 == 0) {
countEven += 1;
} else {
countOdd += 1;
}
}
return countOdd == 1 ? true : false
}
then I plan to loop through the words
let emptyArr = [];
for (var x = 0; x < arr.length; x++) {
if (isPalindrome(arr[x]) {
// not sure what to do here. I know the word is a palindrome but not sure how to sort the order of the word in the palindrome form.
} else {
emptyArr.push(-1);
}
}
return emptyArr;
Look closely: you don't need your words to be palindromes, you need them to be rearrangeable as palindromes ("palindrome-candidates"). Now, a word is a palindrome-candidate if all of its letters but one can be counted by an even number (2, 4, 6 etc.)
For example, this...
hollo
... is NOT a palindrome, but can become one, as there's 2 'o', 2 'l' and just one 'h' in it. To rearrange, you just move 'h' in the middle, then just place 'o' and 'l' before and after it:
l -> o -> h <- o <- l
So start with splitting each of your words by characters, then either count those characters or just sort them (as #Barmar suggested). If they satisfy the condition, rearrange the letters following the approach given; if not, return null (or any other special value clearly distinguishable from the rest) immediately.
Here's one way to do it:
function rearrangeAsPalindrome(word) {
if (word.length === 1) return word; // easy win first
const charCounter = word.split('').reduce((counter, ch) => ({
...counter,
[ch]: (counter[ch] || 0) + 1
}), {});
const parts = ['', '', '']; // left, middle, right
const entries = Object.entries(charCounter);
for (let i = 0; i < entries.length; ++i) {
const [char, counter] = entries[i];
if (counter % 2) { // odd
if (parts[1] !== '') return null;
// one odd is already here, eject! eject!
parts[1] = char.repeat(counter);
}
else { // even
const half = counter / 2;
parts[0] = char.repeat(half) + parts[0];
parts[2] += char.repeat(half);
}
}
return parts.join('');
}
console.log(rearrangeAsPalindrome('racarrrac')); // crraaarrc
console.log(rearrangeAsPalindrome('aabbcc')); // cbaabc
console.log(rearrangeAsPalindrome('hollo')); // lohol
console.log(rearrangeAsPalindrome('hello')); // null
This function returns null (and does it early) when it realizes the word given cannot be rearranged as a palindrome - or an actual palindrome if it is possible.
This can help
"How to generate distinct palindromes from a string in JavaScript"
https://medium.com/#bibinjaimon/how-to-generate-distinct-palindromes-from-a-string-in-javascript-6763940f5138

Sort three numbers in array from least to greatest

I have to create a function that takes 3 numbers. The function should return an array containing the numbers from least to greatest. So far I have this.I know it isn't correct but it's a start.I'm not using native functions as well. Can anyone give me some tips? Appreciate any help.
function leastToGreatest (num) {
var array = [];
var num1 = 0;
var num2 = 0;
var num3 = 0;
for (var i = 0; i < num.length; i++) {
if(num[i] < num[i] && num[i] < num[i]) {
num[i] = num1;
array.push(num1);
}
else if(num[i] > num[i] && num[i] < num[i]) {
num[i] = num2;
array.push(num2);
}
else if(num[i] > num[i] && num[i] > num[i])
num[i] = num3;
array.push(num3);
}
return array;
}
leastToGreatest(2,1,3);
I would suggest using two for loops to solve this problem. For example,
function sortArray(array) {
var temp = 0;
for (var i = 0; i < array.length; i++) {
for (var j = i; j < array.length; j++) {
if (array[j] < array[i]) {
temp = array[j];
array[j] = array[i];
array[i] = temp;
}
}
}
return array;
}
console.log(sortArray([3,1,2]));
With this function, no matter what size the array is it will always sort it.
The reason your function is not working (as is stated by #WashingtonGuedes) is that you are comparing the same value each time. As they said, you will reach the last statement and receive two false's, which causes you to test false for all three if statements. Your returned array, then, will be empty.
One suggestion is to not hard-code for three values, as you have done, but instead assume nothing and let the program do the hard work. As put in my code snippet, you can enter an array of any length and it will be sorted, not just where length is 3.
Your code as it is now currently does not work because you are comparing a number with itself, so it will always be equal (causing your sorting to do nothing). To get a working sort you could fix this or use array.prototype.sort.
Here is a fun little variation on this:
var sortingFunction = function(){
console.log([].slice.apply(arguments).sort(function(a, b) { return a - b; }));
}
sortingFunction(3,2,5,1);
You can pass in as many numbers as you want, not just three. If you want to limit it to three you can test it in the function:
var sortingFunction = function(){
var values = [].slice.apply(arguments);
if(values.length === 3) {
console.log(values.sort(function(a, b) { return a - b; }));
}
else
{
console.log('you didn\'t pass in three values');
}
}
sortingFunction(3,2,5,1);
sortingFunction(3,31,1);
If you just want an array with numbers arranged from least to greatest, you can use the sort() method with the following parameter:
array.sort(function(a,b){return(a-b)});
var array = [12,7,18,1];
array.sort(function(a,b){return (a-b)});
console.log(array); //Array should be arranged from least to greatest
If it's just three items you want to sort, you can do it quite easily with three comparisons and swaps:
if (num[0] > num[1])
{
// swap num[0] and num[1]
temp = num[0]; num[0] = num[1]; num[1] = temp;
}
if (num[0] > num[2])
{
// swap num[0] and num[2]
temp = num[0]; num[0] = num[2]; num[2] = temp;
}
// at this point, num[0] contains the smallest of the three numbers
if (num[1] > num[2])
{
// swap num[1] and num[2]
temp = num[1]; num[1] = num[2]; num[1] = temp;
}
// your three items are sorted
This is easy to prove correct by hand. Write the numbers 1, 2, and 3 on small pieces of paper, lay them out in random order, and then perform those steps above. No matter what order you start with, this will sort those three items.
Understand, the above only works for three items. If you want a way to sort any number of items, then you'll want to use the built-in sorting method.
var sort = function ([x, y, z]) {
var k = [x, y, z];
k[0] = Math.min(x, y, z);
if ((x < y && x > z) || (x < z && x > y)) {
k[1] = x;
}
else if ((y < x && y > z) || (y < z && y > x)) {
k[1] = y;
}
else {
k[1] = z;
}
k[2] = Math.max(x, y, z);
return k;
};

Categories

Resources