I am working through exercises on exercism.io and the third one asks us to compare two DNA strings and return the difference (hamming distance) between them.
So for example:
GAGCCTACTAACGGGAT
CATCGTAATGACGGCCT
^ ^ ^ ^ ^ ^^
There are 7 different characters lined up in that comparison. My question is whether I'm taking the right approach to solve this. I created two empty arrays, created a function that loops through both strings and pushes the different letters when they meet.
I tried running it through a console and I always get an unexpected input error.
var diff = [];
var same = [];
function ham(dna1, dna2) {
for (var i = 0; i < dna1.length; i++)
for (var j = 0; j < dna2.length; i++){
if (dna1[i] !== dna2[j]) {
console.log(dna1[i]);
diff.push(dna1[i]);
}
else {
console.log(dna1[i]);
same.push(dna1[i]);
}
return diff.length;
}
ham("GAGCCTACTAACGGGAT", "CATCGTAATGACGGCCT");
console.log("The Hamming distance between both DNA types is " + diff.length + ".");
Do not use globals.
Do not use nested loops if you don't have to.
Do not store useless things in arrays.
function ham(dna1, dna2) {
if (dna1.length !== dna2.length) throw new Error("Strings have different length.");
var diff = 0;
for (var i = 0; i < dna1.length; ++i) {
if (dna1[i] !== dna2[i]) {
++diff;
}
}
return diff;
}
var diff = ham("GAGCCTACTAACGGGAT", "CATCGTAATGACGGCCT");
console.log("The Hamming distance between both DNA types is " + diff + ".");
The first problem is that you're missing a closing }. I think you want it right before the return statement.
secondly, there's a problem with your algorithm. You compare every item in dna1 (i) with every item in dna2 instead of coparing the item in the same position.
To use a shorter example so we can step through it, consider comparing 'CAT' and 'CBT'. you want to compare the characters in the same position in each string. So you don't actually want 2 for loops, you only want 1. You'd compare C to C ([0]), A to B ([1]), and T to T ( [2] ) to find the 1 difference at [1]. Now step through that with your 2 for loops in your head, and you'll see that you'll get many more differences than exist.
Once you use the same offset for the characters in each string to compare, you have to stat worrying that one might be shorter than the other. You'll get an error if you try to use an offset at the end of the string. So we have to take that into account too, and assumedly count the difference between string length as differences. But perhaps this is out of scope for you, and the the strings will always be the same.
You only need to have one single loop like below:
var diff = [];
var same = [];
function ham(dna1, dna2) {
for (var i = 0; i < dna1.length; i++) {
if (dna1[i] !== dna2[i]) {
console.log("not same");
diff.push(dna1[i]);
} else {
console.log("same");
same.push(dna1[i]);
}
}
return diff.length;
}
ham("GAGCCTACTAACGGGAT", "CATCGTAATGACGGCCT");
console.log("The Hamming distance between both DNA types is " + diff.length + ".");
The edit distance is not really hard to calculate. More code is needed to cover the edge cases in parameter values.
function hamming(str1, str2) {
var i, len, distance = 0;
// argument validity check
if (typeof str1 === "undefined" || typeof str2 === "undefined") return;
if (str1 === null || str2 === null) return;
// all other argument types are assumed to be meant as strings
str1 = str1.toString();
str2 = str2.toString();
// the longer string governs the maximum edit distance
len = str1.length > str2.length ? str1.length : str2.length;
// now we can compare
for (i = 0; i < len; i++) {
if ( !(str1[i] === str2[i]) ) distance++;
}
return distance;
}
Execution of function:
ham( "GAGCCTACTAACGGGAT", "CATCGTAATGACGGCCT" );
of the following function definition:
function ham(A,B){
var D = [], i = 0;
i = A.length > B.length ? A : B;
for( var x in i)
A[x] == B[x] ? D.push(" ") : D.push("^");
console.log( A + "\n" + B +"\n" + D.join("") );
}
will output the log of:
GAGCCTACTAACGGGAT
CATCGTAATGACGGCCT
^ ^ ^ ^ ^ ^^
Is capable of receiving different length strings, which depending on the requirement and data representation comparison can be modified to fill the blank with adequate standard symbols etc.
Demo:
ham("GAGCCTACTAACGGGAT", "CATCGTAATGACGGCCT");
function ham(A, B) {
var D = [],
i = 0;
i = A.length > B.length ? A : B;
for (var x in i)
A[x] == B[x] ? D.push(" ") : D.push("^");
console.log(A + "\n" + B + "\n" + D.join(""));
};
I think that you would want to do something like this:
var dna1 = "GAGCCTACTAACGGGAT";
var dna2 = "CATCGTAATGACGGCCT";
function ham(string1, string2) {
var counter = 0;
for (i = 0;i < string1.length;i++) {
if (string1.slice(i, i + 1) != string2.slice(i, i + 1)) {
counter++
};
};
return(counter);
};
console.log("insert text here " + ham(dna1, dna2));
It checks each character of the string against the corresponding character of the other string, and adds 1 to the counter whenever the 2 characters are not equal.
You can use Array#reduce to iterate the 1st string, by using Function#call, and compare each letter to the letter of the corresponding index in the 2nd string.
function ham(dna1, dna2) {
return [].reduce.call(dna1, function(count, l, i) {
return l !== dna2[i] ? count + 1 : count;
}, 0);
}
var diff =ham("GAGCCTACTAACGGGAT", "CATCGTAATGACGGCCT");
console.log("The Hamming distance between both DNA types is " + diff + ".");
Related
I've seen versions of this question for other languages, but not for JS.
Is it possible to do this recursively in one function?
I understand that I need to take the first element in the string, and then append it to each solution to the recursion on the remainder of the string.
So logically, I understand how the recursion needs to go. I just don't understand how to append the first char onto each of the recursive solutions
var myString = "xyz";
function printPermut(inputString){
var outputString;
if(inputString.length === 0){
return inputString;
}
if(inputString.length === 1){
return inputString;
}
else{
for(int i = 0; i<inputString.length(); i++){
//something here like:
//outputString = outputString.concat(printPermut(inputString.slice(1))??
//maybe store each unique permutation to an array or something?
}
}
}
Let's write a function that returns all permutations of a string as an array. As you don't want any global variables, returning the permutations is crucial.
function permut(string) {
if (string.length < 2) return string; // This is our break condition
var permutations = []; // This array will hold our permutations
for (var i = 0; i < string.length; i++) {
var char = string[i];
// Cause we don't want any duplicates:
if (string.indexOf(char) != i) // if char was used already
continue; // skip it this time
var remainingString = string.slice(0, i) + string.slice(i + 1, string.length); //Note: you can concat Strings via '+' in JS
for (var subPermutation of permut(remainingString))
permutations.push(char + subPermutation)
}
return permutations;
}
To print them, just iterate over the array afterwards:
var myString = "xyz";
permutations = permut(myString);
for (permutation of permutations)
print(permutation) //Use the output method of your choice
Hope I could help you with your question.
The problem of permutations has been studied to death. Heap's algorithm is one well-known solution. Here is a version in JS, using a generator:
function *permute(a, n = a.length) {
if (n <= 1) yield a.slice();
else for (let i = 0; i < n; i++) {
yield *permute(a, n - 1);
const j = n % 2 ? 0 : i;
[a[n-1], a[j]] = [a[j], a[n-1]];
}
}
console.log(Array.from(permute("abcabad".split('')))
.map(perm => perm.join(''))
.filter((el, idx, self) => (self.indexOf(el) === idx)));
permute is designed to take and generate arrays, not strings, so we split the string into characters before calling it, and paste the characters back into strings before printing out the results.
Use Recursive Function to iterate through the string
function getPermutations(string) {
var results = [];
if (string.length === 1)
{
results.push(string);
return results;
}
for (var i = 0; i < string.length; i++)
{
var firstChar = string[i];
var otherChar = string.substring(0, i) + string.substring(i + 1);
var otherPermutations = getPermutations(otherChar);
for (var j = 0; j < otherPermutations.length; j++) {
results.push(firstChar + otherPermutations[j]);
}
}
return results;
}
var permutation = getPermutations('YES').filter((el, idx, self) => (self.indexOf(el) === idx));
console.log("Total permutation: "+permutation.length);
console.log(permutation);
Problem classification: You can look at this problem as an exploration problem, i.e., given a set of input characters explore the different ways you can arrange them.
Solution: Backtracking algorithm excels in solving exploratory problems, although it comes with high time complexity. To demonstrate a solution, imagine how you would solve this problem by hand for a small set of input characters: [a, b, c].
Here are the steps:
Take the left most character. This is the character at index 0 and swap it with target right character at index 0, i.e. with itself. This is because [a, b, c] is a valid permutation on its own therefore we want to keep it. Swapping characters normally requires two pointers which point to each of the characters. So let's say we will have a left and right pointer.
With the same left most character (at index 0) do the swapping with target right character at index 0 + 1 = 1, i.e. move the target right pointer with 1 step further. This will give you the output: [b, a, c]
With the same left most character (at index 0) do the swapping with the next next target right character (i.e. index 0 + 1 + 1 = 2). This will give you the output: [c, b, a]
Ok, now we need to stop as there are no more target right characters to be swapped with the left most character. So our right pointer needs to stay less than the max index in the input. Moving the right pointer with a step at a time we can do with a for loop which starts from the left index and ends with the input length - 1.
Now you need to do exact same steps from above but move the left pointer so that it points to the next left most character. However, keeping the input from step 2 and 3. Another way to imagine this situation is to say: 'Hey, I am done with the left most character. Now I do not want to work with it anymore but I would love to continue with the second left most from the results I have so far.'
When do we stop? When the left pointer has reached the length of the input string - 1, 'cause there is no more characters after this index. In recursive algorithms (such as the backtracking), the case where you need to stop is called base case. In our example the base case is: left === input.length - 1.
Here is a graphical visualisation:
left index| Input String:
-------------------------------------------------------------------------------
left = 0 | in=[a, b, c]
(swap in[0] with in[0]) (swap in[0] with in[1]) (swap in[0] with in[2])
left = 1 | in=[a, b, c] in=[b, a, c] in=[c, b, a]
(swap in[1] with in[1]) (swap in[1] with in[2]) (swap in[1] with in[1])(swap in[1] with in[2]) (swap in[1] with in[1])(swap in[1] with in[2])
left = 2 | [a, b, c] [a, c, b] [b, a, c] [b, c, a] [c, b, a] [c, a, b]
Summary:
To move the left pointer to the right we will use recursive increment
To move the right pointer to the right we will use a for loop, however we need to start always from the left pointer or else we will explore things we have already explored.
Backtracking:
A pseudo-code for backtracking algorithm takes the form of:
fun(input)
if(base_case_check(input)) {
//do final step
} else {
//choose
fun(reduce(input)) //explore
//un-choose
}
Our solution:
function permutate(string) {
if(!string || string.length === 0)
return new Set(['']);
let left = 0;
let result = new Set();
permutationHelper(string, result, left);
return result;
}
function permutationHelper(string, result, left) {
if(left === string.length-1) {
//base case
result.add(string);
} else {
//recursive case
for(let right=left; right < string.length; right++) {
string = swap(string, left, right); //choose
permutationHelper(string, result, left+1); // explore
string = swap(string, left, right); //unchoose
}
}
}
function swap(string, left, right) {
let tmpString = string.split('');
let tmp = tmpString[left];
tmpString[left] = tmpString[right];
tmpString[right] = tmp;
return tmpString.join('');
}
/* End of solution */
/* Tests */
let input = 'abc';
let result = permutate(input);
let expected = new Set(['abc', 'acb', 'bac', 'bca', 'cab', 'cba']);
if(setsEquality(result, expected)) {
console.log('Congrats, you generated all permuations');
} else {
console.log('Sorry, not all permuations are generated');
}
function setsEquality(actualResult, expectedResult) {
if (actualResult.size !== expectedResult.size) {
return false;
}
for (let permutation of actualResult) {
if (!expectedResult.has(permutation)) return false;
}
return true;
}
function assert(condition, desc) {
if (condition) {
console.log(`${desc} ... PASS`);
} else {
console.log(`${desc} ... FAIL`);
}
}
Summary & Time Complexity:
We make our choice by swapping characters in the existing input string
We explore what is left to be explored once we increment our left index with 1. This in fact means that we are reducing our input set for all subsequent recursions with 1. Therefore the work we need to do is: Nx(N-1)x(N-2)x(N-3)x...x1 = N!. However, as we needed a for loop to explore among the input we have, the total time complexity would be: 0(N*N!)
We revert our choice by swapping characters back in the modified input string
permutation=(str,prefix)=>{
if(str.length==0){
console.log(prefix);
}
else{
for(let i=0;i<str.length;i++){
let rem = str.substring(0,i)+str.substring(i+1);
permutation(rem,prefix+str[i]);
}
}
}
let str="ABC";
permutation(str,"");
Semi-Off topic:
random permutation of a given string is as simple as rndperm:
i = document.getElementById("word");
b = document.getElementById("butt");
rndperm = (z) => {
return z.split("").sort(() => ((Math.random() * 3) >> 0) - 1).join("")
}
function scramble() {
i.value = rndperm(i.value);
}
var z;
function sci() {
if (z != undefined) {
clearInterval(z);
b.innerText = "Scramble";
z=undefined;
} else {
z = setInterval(scramble, 100);
b.innerText = "Running...";
}
}
<center><input id="word" value="HelloWorld"></input><button id="butt" onclick=sci()>Scramble</button></center>
I had same question by my interviewer last day but I was not get the correct logic then I came to stackoverflow and I get here but now I have my solution and want to share with all
const str_Permutations = (str,ar = []) => {
str = `${str}`; // ensure type **String**
if(ar.indexOf(str)>-1 || str.length !== (ar.strlen || str.length)) return false; // Checking if value is alreay there or(||) on recursive call string length should not be provided string
ar.strlen = ar.strlen || str.length; // Setting str length of provided value(string)
ar.push(str); // Pushing to array
for(let i = 0; i<str.length;i++){
str_Permutations(str[i] + str.split('').filter(v=>v!==str[i]).join(''),ar);
}
return Array.from(ar); // Removing *strlen* from main result and return **Result** as array
}
str_Permutations("ABC")
//Result: (6) ["ABC", "BAC", "CBA", "BCA", "ACB", "CAB"]
There is used reference feature of Array to hold the values in same Array by passing. I hope you got my point!!!!
const permut = (str) => {
if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str];
return str
.split("")
.reduce(
(acc, letter, i) =>
acc.concat(
permut(str.slice(0, i) + str.slice(i + 1)).map((val) => letter + val)
),
[]
);
};
found here
This does the job, recursively
function printPermutations(str, res='') {
if (!str.length){
console.log(res);
}
for (let i = 0; i < str.length; i++) {
let remStr = str.substr(0, i) + str.substr(i + 1);
printPermutations(remStr, res + str.substr(i, 1));
}
}
printPermutations("abc")
// result
// abc, acb, bac, bca, cab, cba
Simple and readable approach but only limited to 3 chars
const stringPermutation = (str) => {
let permutations = [];
for (let i in str) {
for (let j in str) {
for (let k in str) {
if (str[i] !== str[j] && str[j] !== str[k] && str[i] !== str[k]) {
permutations.push(str[i] + str[j] + str[k]);
}
}
}
}
return permutations;
};
console.log(stringPermutation("abc"));
var str = "abcdefgh";
for(let i = 0; i<str.length; i++){
for(let j = i; j<=str.length; j++){
if(i != j){
var out = str.slice(i,j);
console.log(out);
}
}
}
I was recently doing an interview and was asked multiple questions, one of the questions was this and I had a bit of trouble trying to answer it.
Given a string, find the longest occurrence of vowels "aeiou" that appear.
The substring of vowels do not have to be consecutive, and there can be repeats.
The goal is the find the max occurrence of each vowel and join them, but it must be in the order of "a","e","i","o","u".
Edit: In addition, each individual vowel character must be chained as well. In the example below, there is "aaa" and "aa" , since 3 is longer, our result must contain the longer chain.
For example:
Input: "aaagtaayuhiejjhgiiiouaae"
Result: aaaeiiiou
The code that I have tried is below:
EDIT: Following the solution, I have written this below but I am still running into issues with strings such as "aeiouaaaeeeiiiooouuu". The correct result for that would be 15 but I am getting 5.
var findLongestVowels = function(s){
var count = 1;
var i = 0;
var j = 0;
var vowels = ['a','e','i','o','u'];
var total = 0;
var array = [];
while (i < s.length){
if (s.charAt(i) == vowels[j] && s.charAt(i) == s.charAt(i+1) ){
count++;
}
else if (s.charAt(i) == vowels[j] && s.charAt(i) != s.charAt(i+1)){
if (j === 0 && !array[vowels[j]]){
array[vowels[j]] = count;
}
else if (j === 0 && array[vowels[j]]){
array[vowels[j]] = Math.max(array[vowels[j]],count);
}
else if (j !== 0 && !array[vowels[j]] && array[vowels[j-1]]){
array[vowels[j]] = array[vowels[j-1]] + count;
}
else if (j !== 0 && array[vowels[j]] && array[vowels[j-1]]){
array[vowels[j]] = Math.max(array[vowels[j]],array[vowels[j-1]] + count);
}
count = 1;
}
else if (s.charAt(i) == vowels[j+1] && array[vowels[j]]){
j++;
i--;
}
i++;
}
console.log(array);
console.log('Answer: ' + array[vowels[j]]);
}
findLongestVowels("eeeeebbbagtaagaaajaaaaattyuhiejjhgiiiouaae");
Am I at least going in the right direction?
Thanks in advance.
We can solve this in O(n) time. Consider that for each block, if its vowel is at index v in the list of vowels, we are only interested in the best solution for the block with a vowel at index v-1 in the order of vowels. We save the last best solution for each block type (each vowel) as we go along:
|aaa|g|t|aa|y|u|h|i|e|jj|h|g|iii|o|u|aa|e
b: 1 2 3 4 5 6 7 8 9 10
b 1: v[a] = 3
b 2: v[a] = max(2,3)
b 3: v[u] = None recorded for v-1
b 4: v[i] = None recorded for v-1
b 5: v[e] = 1 + 3
b 6: v[i] = 3 + 4
b 7: v[o] = 1 + 7
b 8: v[u] = 1 + 8 // answer
b 9: v[a] = max(2,3)
b 10: v[e] = 1 + 3
JavaScript code:
function f(str){
console.log(`String: ${ str }\n`);
var vowels = {
a: {best: 0, prev: null},
e: {best: 0, prev: 'a'},
i: {best: 0, prev: 'e'},
o: {best: 0, prev: 'i'},
u: {best: 0, prev: 'o'}
};
function getBlock(i){
let length = 1;
while (str[i+1] && str[i] == str[i+1]){
length++;
i++;
}
return length;
}
for (let i=0; i<str.length;){
let length = getBlock(i);
console.log(`i: ${ i }; length: ${ length }`)
if (!vowels[str[i]]){
i = i + length;
continue;
}
if (!vowels[str[i]].prev){
vowels[str[i]].best = Math.max(
vowels[str[i]].best,
length
);
// make sure the previous vowel
// exists in the string before
// this vowel
} else if (vowels[ vowels[str[i]].prev ].best){
vowels[str[i]].best = Math.max(
vowels[str[i]].best,
length + vowels[ vowels[str[i]].prev ].best
);
}
i = i + length;
}
console.log(`\n${ JSON.stringify(vowels) }\n\n`);
return vowels['u'].best;
}
var s = 'eeeeebbbagtaagaaajaaaaattyuhiejjhgiiiouaae';
console.log(f(s) + '\n\n');
s = 'aaagtaayuhiejjhgiiiouaae';
console.log(f(s) + '\n\n');
s = 'aeiouaaaeeeiiiooouuu';
console.log(f(s));
This problem can be solved by using dynamic programming technique.
First, we have string x and we want to find the longest string for this string.
Traversing the string x from start to end, assuming at index i, we are trying to find vowel e, there are two possibilities:
Current character is e, so we take the whole block and move to next character
Or, we can try with the next character
So, we have our pseudocode:
int[][]dp;
int largestBlock (int index, int currentVowel, String x, String vowels){
if (currentVowel == 5) {
// We found all 5 vowel
return 0;
}
if visited this state (index, currentVowel) before {
return dp[index][currentVowel];
}
int result = largestBlock(index + 1, currentVowel, x, vowels) ;
if (x[index] == vowels[currentVowel]){
int nxt = nextIndexThatIsNotVowel(index, currentVowel, x, vowels);
result = max(result, nxt - index + largestBlock(nxt, currentVowel + 1, x , vowels));
}
return dp[index][currentVowel] = result;
}
Time complexity is O(n * m) with m is number of vowels which is 5 in this case.
You need to remember biggest combination of individual vowels.
Use reduce, map and Object.values
var vowels = "aeiou";
var input = "aaagtaayuhiejjhgiiiouaae";
var output = Object.values(
input.split( "" ).reduce( ( a, c, i, arr ) => {
var lastChar = arr[ i - 1 ];
if ( !vowels.includes( c ) ) return a; //if not vowel, return accumulator
if ( c != lastChar ) //if not same as last character then create a new array
{
a[ c ] = a[ c ] || [];
a[ c ].push( [ c ] );
}
else //else push to the last array;
{
var lastCombo = a[ c ].slice( -1 )[ 0 ];
lastCombo.push(c)
}
return a; //return accumulator
} , {}) ).map( s => {
var char = s[0][0]; //find the character to repeat
var maxLength = Math.max.apply( null, s.map( s => s.length ) ); //find how many times to repeat
return Array( maxLength + 1 ).join( char );
}).join( "" ); //join all the vowels
console.log( output );
It's just one of many possible solutions - feel free to try it out.
Store every vowel you're interested in, in vowels array.
Use map to loop over every vowel in array, create regex from vowel to split string into array of vowels. For example "aaabdmedaskaa" would be split into ["aaa", "a", "aa"].
Filter this array so it doesn't include empty strings.
Sort it by length, so accesing 0 element would give you longest occurance
After mapping over every vowel, return the result - filter out "undefined" in case some of your vowels don't occur at all and regex results in empty array (accesing empty array's 0th element would result in undefined), join array of occurances into a result string.
The regex created from "a" will be [^a]+ which means any character sequence that does not include "a".
function findLongestOccurance(str) {
const vowels = ["a", "e", "i", "o", "u"];
const result = vowels.map(vowel => {
const regex = new RegExp(`[^${vowel}]+`);
return str.split(regex)
.filter(r => r !== "")
.sort((a, b) => b.length - a.length)[0];
});
return result.filter(occ => typeof(occ) !== "undefined").join("");
}
console.log(findLongestOccurance("aaagtaayuhiejjhgiiiouaae"));
Why not regex?
var result = /(a+).*(e+).*(i+).*(o+).*(u+)/.exec("aaagtaayuhiejjhgiiiouaae");
console.log(result[1]+result[2]+result[3]+result[4]+result[5]);
First of all, from what I understand from the question the result for Input: "aaagtaayuhiejjhgiiiouaae" should be aaaaaeiiiou, like #PhamTrung asked in the comments but didn't get answered.
Because it a job interview I would start with the first thing that comes to mind, namely brute force the solution out of this
function a(string, prefix='') {
if(!string.length){
return prefix
}
if(!/[aeiou]/.test(string[0])){
return a(string.substr(1), prefix)
}
const option1 = a(string.substr(1), prefix)
const option2 = a(string.substr(1), prefix+string[0])
const validateRegex = /^a+e+i+o+u+$/
const isValidOption1 = validateRegex.test(option1)
const isValidOption2 = validateRegex.test(option2)
if(isValidOption1 && isValidOption2){
if(option1.length > option2.length) {
return option1
}
return option2
}
if(isValidOption1) {
return option1
}
if(isValidOption2) {
return option2
}
return null
}
const input = 'aaagtaayuhiejjhgiiiouaae'
console.log(a(input))
This has a terrible run time though, we are trying all possible substring that contains only vowels, than we are discarding those that aren't of the form required (a+e+i+o+u+) and than choosing only the biggest of them all. If I'm not mistake this has a worst case of ∑(n choose i) which is O(n^n) - well, the actual worst case here would be a stackOverflow exception for sufficiently large n in which case we'd have to reimplement this with a loop instead of recursing. In this case we could still get an out of memory exception in which case we'd be out of options but to improve our algorithm. It's fair to imagine that if the input were large enough that we got an out of memory exception than our code would also be slow enough to not be a reasonable solution to the problem. I'm just arguing all this because these are things that an interviewer would possibly like to see that you are aware of, meaning you know enough of CS 101.
Following that the interviewer would ask if I can improve the performance. This is a solution with running time of O(n).
const input = 'aeiouaaaeeeiiiooouuu'
let curr = { "1": {price: -1} }
const nodes = []
const voewels = '1aeiou'
const getPrevLetter = (node) => voewels[voewels.indexOf(node.letter) -1]
let resultNode
function setUpNodeByCurrent(node, letter){
node.price = curr[letter].price + 1
node.previous = curr[letter]
}
function setBestResultIfNeeded(node){
if(node.letter !== 'u') {
return
}
if(!resultNode || resultNode.price < node.price) {
resultNode = node
return
}
}
function setCurrent(letter){
const node = {
letter,
price: 0
}
const prevLetter = getPrevLetter(node)
if(!prevLetter || !curr[prevLetter]){
// either letter isn't on of aeiou or
// we got to an i without ever seeing an e, an o without ever seeing an i, ... this letter is irrelevant
return
}
if(curr[node.letter]) {
setUpNodeByCurrent(node, node.letter)
}
if(node.price < curr[prevLetter].price + 1) {
setUpNodeByCurrent(node, prevLetter)
}
curr[node.letter] = node
setBestResultIfNeeded(node)
}
function getStringResult(node){
let result = ''
while(node) {
result = node.letter + result
node = node.previous
}
return result
}
function getResult(){
const node = resultNode //getBestResultNode()
const result = getStringResult(node)
console.log(result)
console.log(result.length)
}
for(let l of input){
setCurrent(l)
}
getResult()
This can be seen as a simplification of the longest path problem over a DAG basically you'd run through the string and every a points to the next occurance of a and the next occurance of e. e points to the next e and to the next i and so on. You'd than have a start node pointing to every occurance of a and an end node pointed to by every occurance of u. Now what you want is the longest path from the start node to the end node which is an O(|V|+|E|), now |V|<=n and |E|<=2n since every node in your graph has at most 2 vertices going out of it so the total running time is O(n). I have simplified the code to build the result as it goes on building the graph, basically I already calculate the cost on the go so when I finished building a graph similar to what I described I already know what the result is.
Note that this solution is based on the assumption that the input string is one that necessarily has a solution embedded in it. If the input string is unsolvable (there isn't and aeiou sequence in it) than this case would need to be properly handled, it is actually trivial to add the code that handles that. The first solution will return null in such a case(if I'm not mistaken)
Hope this helps.
If you want to find a substring which contains the maximum number of vowels and you also want to give {specify} the length of the substring then you should use this program:
let newk = s;
const elementsArray = [];
const tempoArray = [];
const counting = [];
const maxPoint = [];
let count
for (var i = 0; i < newk.length; i++) {
while (tempoArray.length > 0) {
tempoArray.pop();
}
let fk = i + k;
if (fk <= newk.length) {
for (let j = i; j < fk; j++) {
tempoArray.push(newk[j]);
}
let makingArray = tempoArray.toString();
elementsArray.push(makingArray);
} else {
// console.log(" ");
}
}
for (let q = 0; q < elementsArray.length; q++) {
count = 0
let tempString = new String(elementsArray[q]).split(",")
for (let l = 0; l < tempString.length; l++) {
if (tempString[l] == "a" || tempString[l] == "e" || tempString[l] == "i" || tempString[l] == "o" || tempString[l] == "u") {
count ++;
}else{
}
}
// console.log(count);
counting.push(count)
}
let max = 0,Maximist
// for (let d = 0; d < counting.length; d++) {
// console.log(counting[d] , " this is the value of the counting array");
// }
for (let t = 0; t <= counting.length; t++) {
if (counting[t] != 0) {
if (max < counting[t]) {
max = counting[t]
Maximist = t
}
else if (max == counting[t]){
max = counting[t]
Maximist = t
}
else{
console.log("");
}
}
}
// console.log(Maximist);
// console.log(max);
// maxPoint.push(Maximist)
for (let t = 0; t <= counting.length; t++) {
if (counting[0] != 0) {
if (max == counting[t]) {
maxPoint.push(t)
}
}
}
for (let e = 0; e < maxPoint.length; e++) {
console.log("{", elementsArray[maxPoint[e]] ,"}")
}
}
findSubstring("captainamerica", 3);
The bigger your size of the substring will be the less chances that there will be less substring with same number of vowels in it
I'm trying to understand dynamic programming through Levenshtein algorithm, but I have been stuck on this for a few hours now. I know my attempt at the following problem is the 'brute force' one. How would I use "dynamic programming" to change my approach? I'm pretty lost....
Problem: Given two strings, s and t, with lengths of n and m, create a
function that returns one of the following strings: "insert C" if
string t can be obtained from s by inserting character C "delete C"
(same logic as above) "swap c d" if string t can be obtained from
string s by swapping two adjacent characters (c and d) which appear in
that order in the original string. "Nothing" if no operation is
needed "impossible" if none of the above works ie LevenShtein distance is greater than 1.
Here is my brute force attempt. the "tuple" variable is misnamed as I originally wanted to push the indices and values to the matrix but got stuck on that.
function levenshtein(str1, str2) {
var m = str1.length,
n = str2.length,
d = [],
i, j,
vals = [],
vals2 = [];
for (i = 0; i <= m ; i++) {
var tuple = [str1[i]];
//console.log(tuple);
// console.log(tuple);
d[i] = [i];
// console.log(str1[i]);
vals.push(tuple);
}
vals = [].concat.apply([], vals);
vals = vals.filter(function(n){ return n; });
console.log(vals);
for (j = 0; j <= n; j++) {
d[0][j] = j;
var tuple2 = [str2[j]];
// console.log(tuple2);
vals2.push(tuple2);
// console.log(vals2);
}
vals2 = [].concat.apply([], vals2);
vals2 = vals2.filter(function(n){ return n ;});
console.log(vals2);
for (j = 1; j <= n; j++) {
for (i = 1; i <= m; i++) {
if (str1[i - 1] == str2[j - 1]) d[i][j] = d[i - 1][j - 1];
else d[i][j] = Math.min(d[i - 1][j], d[i][j - 1], d[i - 1][j - 1]) + 1;
}
}
var val = d[m][n];
// console.log(d);
if(val > 1){
return "IMPOSSIBLE";
}
if(val === 0){
return "NOTHING";
}
//console.log(d);
if(val === 1){
//find the missing element between the vals
//return "INSERT " + missing element
//find the extra element
//return "DELETE + " extra element
//find the out of place element and swap with another
}
}
console.log(levenshtein("kitten", "mitten"));
// console.log(levenshtein("stop", "tops"));
// console.log(levenshtein("blahblah", "blahblah"));
The problem as described cannot be optimized using dynamic programming because it only involves a single decision, not a series of decisions.
Note that the problem specifically states that you should return "impossible" when the Levenshtein distance is greater than 1, i.e., the strings can't be made equal through a single operation. You need to be searching for a sequence of zero or more operations that cumulatively result in the optimal solution if you want to apply dynamic programming. (This is what the dynamic programming wikipedia article is talking about when it says you need "optimal substructure" and "overlapping subproblems" for dynamic programming to be applicable.)
If you change the problem to calculate the full edit distance between two strings, then you can optimize using dynamic programming because you can reuse the result of choosing to do certain operations at a particular location in the string in order to reduce the complexity of the search.
Your current solution looks a bit overly complex for the given problem. Below a simpler approach you can study. This solution takes advantage of the fact that you know you can only do at most one operation, and you can infer which operation to attempt based off the difference between the lengths of the two strings. We also know that it only makes sense to try the given operation at the point where the two strings differ, rather than at every position.
function lev(s, t) {
// Strings are equal
if (s == t) return "nothing"
// Find difference in string lengths
var delta = s.length - t.length
// Explode strings into arrays
var arrS = s.split("")
var arrT = t.split("")
// Try swapping
if (delta == 0) {
for (var i=0; i<s.length; i++) {
if (arrS[i] != arrT[i]) {
var tmp = arrS[i]
arrS[i] = arrS[i+1]
arrS[i+1] = tmp
if (arrS.join("") == t) {
return "swap " + arrS[i+1] + " " + arrS[i]
}
else break
}
}
}
// Try deleting
else if (delta == 1) {
for (var i=0; i<s.length; i++) {
if (arrS[i] != arrT[i]) {
var c = arrS.splice(i, 1)[0]
if (arrS.join("") == t) {
return "delete " + c
}
else break
}
}
}
// Try inserting
else if (delta == -1) {
for (var i=0; i<t.length; i++) {
if (arrS[i] != arrT[i]) {
arrS.splice(i, 0, arrT[i])
if (arrS.join("") == t) {
return "insert " + arrS[i]
}
else break
}
}
}
// Strings are too different
return "impossible"
}
// output helper
function out(msg) { $("body").append($("<div/>").text(msg)) }
// tests
out(lev("kitten", "mitten"))
out(lev("kitten", "kitten"))
out(lev("kitten", "kitetn"))
out(lev("kiten", "kitten"))
out(lev("kitten", "kittn"))
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I'm playing around with PEG.js.
This is my grammar:
start = expression
expression = a:[a-z]+
{return a.join("");}
When I execute it in my browser:
obj = parser.parse("test");
for (var i = 0; i <= obj.length; i++) {
console.log(i + " - " + obj[i])
}
I get this output:
0 - t
1 - e
2 - s
3 - t
4 - undefined
Why isn't it joined to only 1 word, even though I used return a.join("") in my grammar?
parser.parse does return the single word "test"; you are just printing it one character at a time.
Did you mean to do this?
var result = parser.parse("test");
console.log(result) // "test"
To directly answer your question, you're getting one letter each iteration because a string acts like an array. So you're accessing one letter at a time by using obj[i] Try this to get one word returned.
obj = parser.parse("test");
for (var i = 0; i <= obj.length; i++) {
console.log(i + " - " + obj)
}
What's the best and most efficient way to count keywords in JavaScript? Basically, I'd like to take a string and get the top N words or phrases that occur in the string, mainly for the use of suggesting tags. I'm looking more for conceptual hints or links to real-life examples than actual code, but I certainly wouldn't mind if you'd like to share code as well. If there are particular functions that would help, I'd also appreciate that.
Right now I think I'm at using the split() function to separate the string by spaces and then cleaning punctuation out with a regular expression. I'd also want it to be case-insensitive.
Cut, paste + execute demo:
var text = "Text to be examined to determine which n words are used the most";
// Find 'em!
var wordRegExp = /\w+(?:'\w{1,2})?/g;
var words = {};
var matches;
while ((matches = wordRegExp.exec(text)) != null)
{
var word = matches[0].toLowerCase();
if (typeof words[word] == "undefined")
{
words[word] = 1;
}
else
{
words[word]++;
}
}
// Sort 'em!
var wordList = [];
for (var word in words)
{
if (words.hasOwnProperty(word))
{
wordList.push([word, words[word]]);
}
}
wordList.sort(function(a, b) { return b[1] - a[1]; });
// Come back any time, straaanger!
var n = 10;
var message = ["The top " + n + " words are:"];
for (var i = 0; i < n; i++)
{
message.push(wordList[i][0] + " - " + wordList[i][1] + " occurance" +
(wordList[i][1] == 1 ? "" : "s"));
}
alert(message.join("\n"));
Reusable function:
function getTopNWords(text, n)
{
var wordRegExp = /\w+(?:'\w{1,2})?/g;
var words = {};
var matches;
while ((matches = wordRegExp.exec(text)) != null)
{
var word = matches[0].toLowerCase();
if (typeof words[word] == "undefined")
{
words[word] = 1;
}
else
{
words[word]++;
}
}
var wordList = [];
for (var word in words)
{
if (words.hasOwnProperty(word))
{
wordList.push([word, words[word]]);
}
}
wordList.sort(function(a, b) { return b[1] - a[1]; });
var topWords = [];
for (var i = 0; i < n; i++)
{
topWords.push(wordList[i][0]);
}
return topWords;
}
Once you have that array of words cleaned up, and let's say you call it wordArray:
var keywordRegistry = {};
for(var i = 0; i < wordArray.length; i++) {
if(keywordRegistry.hasOwnProperty(wordArray[i]) == false) {
keywordRegistry[wordArray[i]] = 0;
}
keywordRegistry[wordArray[i]] = keywordRegistry[wordArray[i]] + 1;
}
// now keywordRegistry will have, as properties, all of the
// words in your word array with their respective counts
// this will alert (choose something better than alert) all words and their counts
for(var keyword in keywordRegistry) {
alert("The keyword '" + keyword + "' occurred " + keywordRegistry[keyword] + " times");
}
That should give you the basics of doing this part of the work.
Try to split you string on words and count the resulting words, then sort on the counts.
This builds upon a previous answer by insin by only having one loop:
function top_words(text, n) {
// Split text on non word characters
var words = text.toLowerCase().split(/\W+/)
var positions = new Array()
var word_counts = new Array()
for (var i=0; i<words.length; i++) {
var word = words[i]
if (!word) {
continue
}
if (typeof positions[word] == 'undefined') {
positions[word] = word_counts.length
word_counts.push([word, 1])
} else {
word_counts[positions[word]][1]++
}
}
// Put most frequent words at the beginning.
word_counts.sort(function (a, b) {return b[1] - a[1]})
// Return the first n items
return word_counts.slice(0, n)
}
// Let's see if it works.
var text = "Words in here are repeated. Are repeated, repeated!"
alert(top_words(text, 3))
The result of the example is: [['repeated',3], ['are',2], ['words', 1]]
I would do exactly what you have mentioned above to isolate each word. I would then probably add each word as the index of an array with the number of occurrences as the value.
For example:
var a = new Array;
a[word] = a[word]?a[word]+1:1;
Now you know how many unique words there are (a.length) and how many occurrences of each word existed (a[word]).