I'm new on this and javascript. I've tried to solve an exercise that consist to find repeated letter a in an array. The way to do is use basic structures (no regex neither newer ways of javascript (only ES5)). I need to do it this way to understand the bases of the language.
The output must be this:
//Captain America, the letter 'C' => 2 times.
//Captain America, the letter 'A' => 4 times.
//Captain America, the letter 'I' => 2 times.
I'm not looking for the solution, only the way to do it and its logical structures. Any suggestions are welcome.
My way but it doesn't work:
function duplicateLetter(name) {
var newArray = [];
for (var i=0; i<name.length; i++) {
console.log(name[i].indexOf(newArray));
if (name[i].indexOf(newArray) === 0) {
newArray.push(name[i]);
}
}
console.log(newArray);
//console.log(name + ", the letter '" + (newArray[0]).toUpperCase() + "' => " + newArray.length + " times");
}
duplicateLetter("Captain America");
function duplicateLetter(o) {
var arr = o.toUpperCase().split('');
var obj = {};
for(var v in arr) {
obj[arr[v]] = obj[arr[v]] || 0;
obj[arr[v]]++;
}
for(var v in obj) {
console.log(o + ", the letter '" + v + "' => " + obj[v] + ' times.');
}
}
duplicateLetter("Captain America");
The explanation:
We make the string upper case, then turn it into an array of letters.
We loop over the array, here, arr[v] becomes our letter, and:
If the key arr[v] doesn't exist in our object, we set it to 0.
We increment the value of the key arr[v] in our object (this causes obj['c'] to increment every time our letter is c. You can notice that this keeps track of the number of letters in our string.
We loop over the object v, printing the number of occurrences of each letter to console.
Note that this considers the space character as a letter. If you want an answer that doesn't, please specify so.
Here's a different answer that doesn't use objects and only counts letters (and not spaces or punctuation) to prove that everything is possible in more than one way.
// Not using objects and not counting anything but letters.
function duplicateLetter(o) {
var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var arr = o.toUpperCase().split('');
var count = [];
for(var v in arr) {
pos = letters.indexOf(arr[v]);
if(pos < 0) continue; // It wasn't a letter.
count[pos] = count[pos] || 0;
count[pos]++;
}
for(var v in count) {
if(!(count[v] > 0)) continue; // The letter never appeared.
console.log(o + ", the letter '" + letters[v] + "' => " + count[v] + ' times.');
}
}
duplicateLetter("Captain America");
I could probably also attempt an answer that doesn't use arrays at all!
Edit:
You can use for(a in b) loops to iterate arrays as well as objects. This is because an array is really just an object in which all enumerable properties have integer indices:
arr = [10,15,"hi"] is almost the same as arr = {'0' : 10, '1' : 15, '2' : "hi"} in the way Javascript works internally. Therefore, for (v in arr) will iterate over the array normally.
As requested, the same answer with normal for loops:
// Not using objects and not counting anything but letters.
function duplicateLetter(o) {
var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var arr = o.toUpperCase().split('');
var count = [];
for(var v = 0; v < arr.length; v++) {
pos = letters.indexOf(arr[v]);
if(pos < 0) continue; // It wasn't a letter.
count[pos] = count[pos] || 0;
count[pos]++;
}
for(var v = 0; v < count.length; v++) {
if(!(count[v] > 0)) continue; // The letter never appeared.
console.log(o + ", the letter '" + letters[v] + "' => " + count[v] + ' times.');
}
}
duplicateLetter("Captain America");
Note that nothing changed outside what was in the for brackets. The for-in notation is just easier for the human brain to comprehend, in my opinion, and is the reason I used it.
As for count[pos] = count[pos] || 0;, explaining why it works the way it does is extremely tedious, since it requires that you know precisely what the || operator does. So I'm just going to state what it does, without explaining it.
Basically, count[pos] = count[pos] || 0; is the same as:
if(count[pos]) { // If count[pos] evaluates to true.
count[pos] = count[pos]
} else { // count[pos] is false, '', null, undefined, 0, or any other value that evaluates to false.
count[pos] = 0;
}
Note that this works because at the start, count[pos] is undefined (count is an empty array), so it puts a 0 in it. If we find the letter again, count[pos] is defined, and is a positive value, and therefore evaluates to true, so we don't change it.
Just consider a = a || b to be equal to:
Put the default value of b into a if a is undefined (or evaluates to false by any other means).`
Make an object whose keys are the letters and values are the number of times that letter was repeated. For example,
'abbc' => {'a': 1, 'b': 2, 'c': 1}
Related
Write a function that reverses characters in (possibly nested) parentheses in the input string.
Input strings will always be well-formed with matching ()s.
For inputString = "(bar)", the output should be
reverseInParentheses(inputString) = "rab";
For inputString = "foo(bar)baz", the output should be
reverseInParentheses(inputString) = "foorabbaz";
For inputString = "foo(bar(baz))blim", the output should be
reverseInParentheses(inputString) = "foobazrabblim".
[input] string inputString
A string consisting of lowercase English letters and the characters ( and ). It is guaranteed that all parentheses in inputString form a regular bracket sequence.
Guaranteed constraints:
0 ≤ inputString.length ≤ 50.
[output] string
Return inputString, with all the characters that were in parentheses reversed.
My Solution
Java Script
function reverseInParentheses(inputString) {
let arr = inputString
let start = arr.indexOf(')') < arr.lastIndexOf('(') ? arr.indexOf('(') : arr.lastIndexOf('(')
let end = arr.indexOf(')')
let temp = arr.substring(start + 1, end)
if(start !== -1 && end !== -1){
return reverseInParentheses(arr.substring(0, start) +
[...temp].reverse().join('') +
arr.substring(end + 1))
}
return arr
}
Problem
I am passing all cases except for final hidden case, no runtime or execution time limit error is being returned. So I am having trouble figuring out what scenario is causing the fail. I really want to use my own solution instead of copying the regex ones and in my mind this solution should work, perhaps a more experienced mind can show my folly. Thanks in advance.
The problem is that your calculation of start and end really don't work. And there's no simple fix to this problem.
The comment from Jonas Wilms suggests trying '((see)(you))'. For this test case, you will get start and end like this:
0 5
((see)(you))
^ ^
start ----' '---- end
Note that the start and end are not an actual pair here. There's another '(' in between.
You can fix this up by doing a more sophisticated calculation of these values, by iterating through the characters, updating start every time you hit a '(' and updating end when you hit a ')', then stopping.
That might look like this:
function reverseInParentheses(inputString) {
let arr = inputString
let i = 0, start = 0, end = -1
while (end < start && i < arr.length) {
if (arr[i] == '(') {start = i}
if (arr[i] == ')') {end = i}
i++
}
let temp = arr.substring(start + 1, end)
if(start !== -1 && end !== -1){
return reverseInParentheses(arr.substring(0, start) +
[...temp].reverse().join('') +
arr.substring(end + 1))
}
return arr
}
console .log (reverseInParentheses('(bar)'))
console .log (reverseInParentheses('foo(bar)baz'))
console .log (reverseInParentheses('foo(bar(baz))blim'))
console .log (reverseInParentheses('((see)(you))'))
I don't particularly like this, combining the iteration to find the parentheses with recursion to keep reapplying the function until there are none left. It feels awkward.
There are other solutions, as you noted. One would be to use regular expressions. Note that the language of balanced parentheses is not a regular language, and hence cannot be captured by any one regular expression, but you can repeatedly apply regular expression operations in an iteration or a recursion to get this to work. Here is one version of that.
const rev = ([...cs]) => cs.reverse().join('')
const reverseInParentheses = (s) =>
/\(([^)]*)\)/ .test (s)
? reverseInParentheses (s .replace(/(.*)\(([^)]*)\)(.*)/, (_, a, b, c) => a + rev(b) + c))
: s
console .log (reverseInParentheses('(bar)'))
console .log (reverseInParentheses('foo(bar)baz'))
console .log (reverseInParentheses('foo(bar(baz))blim'))
console .log (reverseInParentheses('((see)(you))'))
Briefly, this finds innermost pairs of parentheses, replaces them with the reversal of their content, then recurs on the result, bottoming out when there are no more pairs found.
This solution was thrown together, and there are probably better regular expressions operations available.
But I actually prefer a different approach altogether, treating the characters of the string as events for a simple state machine, with a stack of nested parenthesized substrings. Here is what I wrote:
const reverseInParentheses = ([c, ...cs], res = ['']) =>
c == undefined
? res [0]
: c == '('
? reverseInParentheses (cs, [...res, ''])
: c == ')'
? reverseInParentheses (cs, [...res.slice(0, -2), res[res.length - 2] + [...res[res.length - 1]].reverse().join('')])
: reverseInParentheses (cs, [...res.slice(0, -1), res[res.length - 1] + c])
console .log (reverseInParentheses('(bar)'))
console .log (reverseInParentheses('foo(bar)baz'))
console .log (reverseInParentheses('foo(bar(baz))blim'))
console .log (reverseInParentheses('((see)(you))'))
We can examine the behavior by adding this as the first line of the body expression:
console .log (`c: ${c ? `"${c}"` : '< >'}, cs: "${cs.join('')}", res: ["${res.join('", "')}"]`) ||
For '((see)(you))', we would get something like this:
curr (c)
remaining (cs)
stack (res)
"("
"(see)(you))"
[""]
"("
"see)(you))"
["", ""]
"s"
"ee)(you))"
["", "", ""]
"e"
"e)(you))"
["", "", "s"]
"e"
")(you))"
["", "", "se"]
")"
"(you))"
["", "", "see"]
"("
"you))"
["", "ees"]
"y"
"ou))"
["", "ees", ""]
"o"
"u))"
["", "ees", "y"]
"u"
"))"
["", "ees", "yo"]
")"
")"
["", "ees", "you"]
")"
""
["", "eesuoy"]
< >
< >
["yousee"]
I choose to process this state machine recursively, because I prefer working with immutable data, not reassigning variables, etc. But this technique should work equally well with an iterative approach.
String reverseInParentheses(String inputString) {
//recursion
int start = -1;
int end = -1 ;
for(int i = 0; i < inputString.length(); i++){
if(inputString.charAt(i) == '('){
start = i;
}
if(inputString.charAt(i) == ')'){
end = i;
String reverse = new StringBuilder(inputString.substring(start+1, end)).reverse().toString();
return reverseInParentheses(inputString.substring(0, start) + reverse+ inputString.substring(end+1));
}
}
return inputString;
}
function solution(inputString) {
let s;
let e = 0;
while (e < inputString.length) {
//if we saw a ')', we mark the index as e, then we go back the
//nearest '(', and mark the index as s
if (inputString[e] === ')') {
s = e;
while (inputString[s] !== '(') {
s--;
}
//get the string in the parenthesis
let beforeRevert = inputString.slice(s + 1, e);
//revert it
let reversed = beforeRevert.split('').reverse().join('');
//put pieces together to get a new inputString
inputString = inputString.slice(0, s) + reversed +
inputString.slice(e + 1, inputString.length)
//because we get rid of the '(' and ')', now we are at index e-1 of
//new inputString
e--;
} else {
e++;
}
}
return inputString;
}
You could try this. It worked for me.
function solution(s) {
while (true) {
let c = s.indexOf(")");
if (c === -1) {
break;
}
let o = s.substring(0, c).lastIndexOf("(");
let start = s.substring(0, o);
let middle = s.substring(o + 1, c).split("").reverse().join("");
let end = s.substring(c + 1, s.length);
s = start + middle + end;
}
return s;
}
If I have a string a12c56a1b5 then out put should be a13b5c56 as character a is repeated twice so a12 becomes a13
I have tried this:
function stringCompression (str) {
var output = '';
var count = 0;
for (var i = 0; i < str.length; i++) {
count++;
if (str[i] != str[i+1]) {
output += str[i] + count;
count = 0;
}
}
console.log(output); // but it returns `a11121c15161a111b151` instead of `a13b5c56`
}
It is happening because the code is counting the occurrence of each element and appending it, even the numbers in the string.
In this code,
for (var i = 0; i < str.length; i++) {
count++;
if (str[i] != str[i+1]) {
output += str[i] + count;
count = 0;
}
}
in first iteration i = 0, str[i] = 'a' and str[i + 1] = '1' for the given string a12c56a1b5 which are not equal hence, it will generate the output as a1 for first iteration, then a111 for second iteration since str[i] = '1' and str[i + 1] = '2' now, and so on.
We can achieve this by first separating the characters from the count. Assuming, that there would be characters from a-z and A-Z only followed by the count. We can do something like this, str.match(/[a-zA-Z]+/g) to get the characters: ["a", "c", "a", "b"] and str.match(/[0-9]+/g) to get their counts: ["12", "56", "1", "5"], put them in an object one by one and add if it already exists.
Something like this:
function stringCompression(str) {
var characters = str.match(/[a-zA-Z]+/g);
var counts = str.match(/[0-9]+/g);
var countMap = {};
for (var i = 0; i < characters.length; i++) {
if (countMap[characters[i]]) {
countMap[characters[i]] += parseInt(counts[i]);
} else {
countMap[characters[i]] = parseInt(counts[i]);
}
}
var output = Object.keys(countMap)
.map(key => key + countMap[key])
.reduce((a, b) => a + b);
console.log(output);
}
stringCompression('a12c56a1b5')
Using regex to extract word characters and numbers. Keeps an object map res to track and sum up following numbers. sorts and converts back to a string.
As an example, the for-of loop iteration flow with str=a12c56a1b5:
c='a', n='12'
res['a'] = (+n = 12) + ( (res['a'] = undefined)||0 = 0)
or ie: res['a'] = 12 + 0
c='c', n='56'
res['c'] = 56 + 0
c='a', n='1'
res['a'] = 1 + (res['a'] = 12 from iteration 1.) = 13
c='b', n='5'
res['b'] = 5 + 0
thus res = { 'a': 13, 'c': 56, 'b': 5 } after the for-of loop finishes
function stringCompression (str) {
// build object map with sums of following numbers
const res = {}
for(const [,c,n] of str.matchAll(/(\w+)(\d+)/g))
res[c] = +n + (res[c]||0)
// convert object map back to string
output = Object.entries(res)
output.sort(([a],[b])=>a<b ? -1 : a>b ? 1 : 0)
output = output.map(([a,b])=>`${a}${b}`).join('')
console.log(output); // but it returns `a11121c15161a111b151` instead of `a13b5c56`
}
stringCompression('a12c56a1b5')
[,c,n] = [1,2,3] is equivalent to c=2, n=3. It is called destructuring.
matchAll matches on a regex. It's a relatively new shorthand for calling .exec repeatedly to execute a regular expression that collects all the results that the regular expression matches on.
(\w+)(\d+) is a regex for two capture groups,
\w+ is for one or more alpha characters, \d+ is for one or more digits.
for(const [,c,n] of str.matchAll...) is equivalent to:
for each M of str.matchAll...
const c = M[1], n = M[2]`
res[c]||0 is shorthand for:
"give me res[c] if it is truthy (not undefined, null or 0), otherwise give me 0"
+n uses the unary operator + to force an implicit conversion to a number. JavaScript specs for + unary makes it convert to number, since + unary only makes sense with numbers.
It is basically the same as using Number(n) to convert a string to an number.
Conversion back to a string:
Object.entries converts an object {"key":value} to an array in the form of [ [key1, value1], [key2, value2] ]. This allows manipulating the elements of an object like an array.
.sort sorts the array. I destructured the keys to sort on the keys, so "a" "b" "c" are kept in order.
.map takes an array, and "maps" it to another array. In this case I've mapped each [key,value] to a string key+value, and then taking the final mapped array of key+value strings and joined them together to get the final output.
In case it asks you to sort it alphabetically, I added #user120242's sorting code snippet to #saheb's entire answer (in between Object.keys(countMap) and .map(...). That worked for me. I tried using #user120242's whole answer, but it did not pass all the tests since it did not add the repeated letters for longer strings. But #user120242's answer did work. It just need to be sorted alphabetically and it passed all the test cases in HackerRank. I had this question for a coding assessment (called "Better Coding Compression").
P.S. I also removed checking the capital letters from #saheb's code since that wasn't required for my coding challenge.
Here's how mine looked like:
function stringCompression(str) {
var characters = str.match(/[a-zA-Z]+/g);
var counts = str.match(/[0-9]+/g);
var countMap = {};
for (var i = 0; i < characters.length; i++) {
if (countMap[characters[i]]) {
countMap[characters[i]] += parseInt(counts[i]);
} else {
countMap[characters[i]] = parseInt(counts[i]);
}
}
var output = Object.keys(countMap)
.sort(([a],[b])=>a<b ? -1 : a>b ? 1 : 0)
.map(key => key + countMap[key])
.reduce((a, b) => a + b);
console.log(output);
}
stringCompression('a12c56a1b5')
Thanks to Nina I have a code to compare two sentences word by word and return the number of word matches like this:
function includeWords(wanted, seen) {
var wantedMap = wanted.split(/\s+/).reduce((m, s) => m.set(s, (m.get(s) || 0) + 1), new Map),
wantedArray = Array.from(wantedMap.keys()),
count = 0;
seen.split(/\s+/)
.forEach(s => {
var key = wantedArray.find(t => s === t || s.length > 3 && t.length > 3 && (s.startsWith(t) || t.startsWith(s)));
if (!wantedMap.get(key)) return;
console.log(s, key)
++count;
wantedMap.set(key, wantedMap.get(key) - 1);
});
return count;
}
let matches = includeWords('i was sent to earth to protect you introduced', 'they\'re were protecting him i knew that i was aware introducing');
console.log('Matched words: ' + matches);
The code works fine, but there is still one issue:
What if we want to return a match for introduced and introducing too?
If you want the program to consider the words 'introduce' and 'introducing' as a match, it would amount to a "fuzzy" match (non binary logic). One simple way of doing this would require more code, the algorithm of which would possibly resemble
Take 2 words that you wish to match, tokenize into ordered list
of letters
Compare positionally the respective letters, i.e
match a[0]==b[0]? a[1]==b[1] where a[0] represents the first letter
of the first word and b[0] represents the first tokenized
letter/character potential match candidate
KEep a rolling numeric count of such positional matches. In this case it is 8 (introduc).
divide by word length of a = 8/9 call this f
divide by word length of b = 8/11 call this g
Provide a threshold value beyond which the program will consider it a match. eg. if you say anything above 70% in BOTH f and g can be
considered a match - viola, you have your answer!
Please note that there is some normalization also needed to prevent low length words from becoming false positives. you can add a constraint that the aforementioned calculation applies to words with at least 5 letters(or something to that effect!
Hope this helps!!
Regards,
SR
You could calculate similarites for a word pair and get a relation how many characters are similar bei respecting the length of the given word and the wanted pattern.
function getSimilarity(a, b) {
var i = 0;
while (i < a.length) {
if (a[i] !== b[i]) break;
i++;
}
return i / Math.max(a.length, b.length);
}
console.log(getSimilarity('abcdefghij', 'abc')); // 0.3
console.log(getSimilarity('abcdefghij', 'abcdef')); // 0.6
console.log(getSimilarity('abcdefghij', 'abcdefghij')); // 1
console.log(getSimilarity('abcdef', 'abcdefghij')); // 0.6
console.log(getSimilarity('abcdefghij', 'abcdef')); // 0.6
console.log(getSimilarity('abcdefghij', 'xyz')); // 0
console.log(getSimilarity('introduced', 'introducing')); // 0.7272727272727273
Here's a quick fix solution.
It's not intended as a complete solution.
Since the English language has more than a few quirks that would almost require an AI to understand the language.
First add a function that can compare 2 words and returns a boolean.
It'll also make it easier to test for specific words, and adapt to what's really needed.
For example, here's a function that does the simple checks that were already used.
Plus an '...ed' versus '...ing' check.
function compareWords (word1, word2) {
if (word1 === word2)
return true;
if (word1.length > 3 && word2.length > 3) {
if (word1.startsWith(word2) || word2.startsWith(word1))
return true;
if (word1.length > 4 && word2.length > 4) {
if (/(ing|ed)$/.test(word1) && word1.replace(/(ing|ed)$/, 'inged') === word2.replace(/(ing|ed)$/, 'inged'))
return true;
}
}
return false;
}
//
// tests
//
let words = [
["same", "same"],
["different", "unsame"],
["priced", "pricing"],
["price", "priced"],
["producing", "produced"],
["produced", "producing"]
];
words.forEach( (arr, idx) => {
let word1= arr[0];
let word2= arr[1];
let isSame = compareWords(word1, word2);
console.log(`[${word1}] ≈ [${word2}] : ${isSame}`);
});
Then use it in the code you already have.
...
seen.split(/\s+/)
.forEach(s => {
var key = wantedArray.find(t => compareWords(t, s));
...
Regarding string similarity, here's f.e. an older SO post that has some methods to compare strings : Compare Strings Javascript Return %of Likely
I have implemented this, it seems to work fine. any suggestions would be appreciated..
let speechResult = "i was sent to earth to introducing protect yourself introduced seen";
let expectSt = ['they were protecting him knew introducing that you i seen was aware seen introducing'];
// Create arrays of words from above sentences
let speechResultWords = speechResult.split(/\s+/);
let expectStWords = expectSt[0].split(/\s+/);
function includeWords(){
// Declare a variable to hold the count number of matches
let arr = [];
for(let a = 0; a < speechResultWords.length; a++){
for(let b = 0; b < expectStWords.length; b++){
if(similarity(speechResultWords[a], expectStWords[b]) > 69){
arr.push(speechResultWords[a]);
console.log(speechResultWords[a] + ' includes in ' + expectStWords[b]);
}
} // End of first for loop
} // End of second for loop
let uniq = [...new Set(arr)];
return uniq.length;
};
let result = includeWords();
console.log(result)
// The algorithmn
function similarity(s1, s2) {
var longer = s1;
var shorter = s2;
if (s1.length < s2.length) {
longer = s2;
shorter = s1;
}
var longerLength = longer.length;
if (longerLength == 0) {
return 1.0;
}
return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength)*100;
}
function editDistance(s1, s2) {
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();
var costs = new Array();
for (var i = 0; i <= s1.length; i++) {
var lastValue = i;
for (var j = 0; j <= s2.length; j++) {
if (i == 0)
costs[j] = j;
else {
if (j > 0) {
var newValue = costs[j - 1];
if (s1.charAt(i - 1) != s2.charAt(j - 1))
newValue = Math.min(Math.min(newValue, lastValue),
costs[j]) + 1;
costs[j - 1] = lastValue;
lastValue = newValue;
}
}
}
if (i > 0)
costs[s2.length] = lastValue;
}
return costs[s2.length];
}
I am working on some challenges on HackerRank and I am having some troubles with making functions faster/more efficient so that it does not timeout during the submit process. It usually times out for really large inputs (ex: string length of 1000 or more) with the number of loops I am using to get the function working. I know the loops make the complexity 0(n * n) or 0(n * n * n). I understand why the function is timing out because of the above complexity issue but I am not sure of how to make the function more efficient in order to handle larger inputs. I am a self-taught coder so please explain any answers thoroughly and simply so I can learn. Thanks!
Here is an example problem:
A string is said to be a special palindromic string if either of two conditions is met:
All of the characters are the same, e.g. aaa.
All characters except the middle one are the same, e.g. aadaa. (acdca will not satisfy this rule but aadaa will)
A special palindromic substring is any substring of a string which meets one of those criteria. Given a string, determine how many special palindromic substrings can be formed from it.
For example, given the string s = mnonopoo, we have the following special palindromic substrings:
m, n, o, n, o, p, o, o
oo
non, ono, opo
Function Description
Complete the substrCount function in the editor below. It should return an integer representing the number of special palindromic substrings that can be formed from the given string.
substrCount has the following parameter(s):
n: an integer, the length of string s
s: a string
function substrCount(n, s) {
//if each letter is its own palindrome then can start with length for count
let count = n;
//space used to get the right string slices
let space = 1;
//so we only get full strings with the split and no duplicates
let numberToCount = n;
for(let i = 0; i < n; i++){
for(let j = 0; j < n; j++){
//slice the string into the different sections for testing if palindrome
let str = s.slice(j, j+space);
if(numberToCount > 0){
//if it is an even length the all characters must be the same
if(str.length % 2 === 0){
let split = str.split('');
let matches = 0;
for(let k = 0; k < split.length; k++){
if(split[k] === split[k+1]){
matches++;
}
}
if(matches === split.length -1){
count++;
}
//if it is not even then we must check that all characters on either side
//of the middle are all the same
} else {
if(str.length > 1){
let splitMid = Math.floor(str.length / 2);
let firstHalf = str.slice(0, splitMid);
let lastHalf = str.slice(splitMid+1, str.length);
if(firstHalf === lastHalf){
if(str.length === 3){
count++;
} else {
let sNew = firstHalf + lastHalf;
let split = sNew.split('');
let matches = 0;
for(let k = 0; k < split.length; k++){
if(split[k] === split[k+1]){
matches++;
}
}
if(matches === split.length -1){
count++;
}
}
}
}
}
}
numberToCount--;
}
numberToCount = n-space;
space++;
}
return count;
}
i came up with a solution that i think is not too complex in terms of performance(one loop and a recursion at a time)
steps
split string and insert it into an array
check first for even pairs into a recursion
next check for odd pairs again into a recursion
check that the values inserted to final array are unique(not unique only for single chars)
please let me know if this is the correct solution or we can speed it up
const stirng = "mnonopoo";
const str = stirng.split("");
let finalArray = [];
str.forEach((x, index) => {
if (str[index] === str[index + 1]) {
checkEven(str, index, 1)
}
if (str[index - 1] === str[index + 1]) {
checkOdd(str, index, 0)
}
finalArray.push(x);
})
function checkOdd(str1, index, counter) {
if (str1[index - counter] === str1[index + counter]) {
counter++;
checkOdd(str1, index, counter);
} else {
pushUnique(finalArray, str1.slice(index - counter + 1, index + counter).join(""));
return str1.slice(index - counter, index + counter).join("")
}
}
function checkEven(str1, index, counter) {
if (str1[index] === str1[index + counter]) {
counter++;
checkEven(str1, index, counter);
} else {
pushUnique(finalArray, str1.slice(index, index + counter).join(""));
return;
}
}
function pushUnique(array, value) {
if (array.indexOf(value) === -1) {
array.push(value);
}
}
console.log(finalArray)
Since your only looking for special palindromes, and not all palindromes, that makes reducing complexity a bit easier, but even then, there will be some special cases, like "abababababababa....". No way I can see to reduce the complexity of that one too far.
I'd approach this like so. Start by grouping all the repeating numbers. I'm not sure of the best way to do that, but I'm thinking maybe create an array of objects, with object properties of count and letter.
Start with your totalCount at 0.
Then, select all objects with a count of 1, and check the objects to the left and right of them, and if they have the same letter value, take the MAX count, and add that value + 1 to your totalCount (the +1 being to account for the single letter by itself). If the letter values on either side do not match, just add 1 (to account for the letter by itself).
That handles all the odd number palindromes. Now to handle the even number palindromes.
Select all the objects with a count > 1, and take their count value and add the series from 1-count to the totalCount. The formula for this is (count/2)*(1+count).
Example:
In the string you have a run of 4 A's. There are the special palindromes (a, a, a, a, aa, aa, aa, aaa, aaa, aaaa) in that, for a total of 10. (4/2)*(1+4)=10.
I don't know how much that will reduce your processing time, but I believe it should reduce it some.
I've looked this up online without much results because it's quite hard to describe in a few words.
Basically, I need to have a function, say puntil which takes the argument string. Basically, the function permutes until the string is equal to the argument.
For example if you run puntil('ab') it should do inside the function:
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
aa
ab !! MATCH
Another example, for puntil('abcd') it will do
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
aa
ab
ac
ad
ae
af
ag
ah
ai
aj
ak
al
am
an
ao
ap
aq
ar
as
at
au
av
aw
ax
ay
az
... etc etc ..
until it matches abcd.
Basically an infinite permutation until it matches.
Any ideas?
Here is the fiddle
var alphabet = ['a','b','c'];//,'d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
var output = "";
var match = "cccc"; //<----------- match here
//This is your main function
function permutate() {
var d = 0; // d for depth
while (true) {
//Your main alphabet iteration
for (var i=0; i<alphabet.length; i++){
//First level
if (d === 0) {
console.log(alphabet[i])
output = alphabet[i];
}
else
iterator(alphabet[i], d); //Call iterator for d > 0
if (output === match)
return;
}
d++; //Increase the depth
}
}
//This function runs n depths of iterations
function iterator(s, depth){
if (depth === 0)
return;
for (var i=0; i<alphabet.length; i++){
if (depth > 1)
iterator(s + alphabet[i], depth - 1)
else {
console.log(s + alphabet[i]);
output = s + alphabet[i];
}
if (output === match)
return;
}
};
Explanation:
Your program needs to traverse a tree of alphabet like this
[a]
-[a]
-[a]
-[a]...
-[b]...
[b] ...
-[b] -
-[a]...
-[b]...
[b] - ...
[c] - ...
This could have easily been done through a conventional recursive function if not for the requirement that you need to finish each depth first.
So we need a special iterator(s, depth) function which can perform number of nested iterations (depth) requested.
So the main function can call the iterator with increasing depths (d++).
That's all!!
Warning: This is a prototype only. This can be optimized and improved. It uses global variables for the ease of demonstrating. Your real program should avoid globals. Also I recommend calling the iterator() inside setTimeout if your match word is too long.
The n depths can only be limited by your resources.
Fiddle here
function next(charArray, rightBound){
if(!rightBound){
rightBound = charArray.length;
}
var oldValue = charArray[rightBound-1];
var newValue = nextCharacter(charArray[rightBound-1]);
charArray[rightBound-1] = newValue;
if(newValue < oldValue){
if(rightBound > 1){
next(charArray, rightBound-1);
}
else{
charArray.push('a');
}
}
return charArray;
}
function nextCharacter(char){
if(char === 'z'){
return 'a'
}
else{
return String.fromCharCode(char.charCodeAt(0) + 1)
}
}
function permuteUntil(word){
var charArray = ['a'];
var wordChain = ['a'];
while(next(charArray).join('') !== word){
wordChain.push(charArray.join(''));
}
wordChain.push(word);
return wordChain.join(', ');
}
alert(permuteUntil('ab'));
What OP is asking is a bit ambiguous, so I'll post for both the things (that I doubt) OP is asking.
First, the question can be, what will be the position of input string in the infinite permutation of alphabets (which I see as more legit question, I've given the reason later). This can be done in the following manner:
Taking an example (input = dhca). So, all strings of 1 to 3 characters length will come before this string. So, add: 26^1 + 26^2 + 26^3 to the answer. Then, 1st character is d, which means, following the dictionary, if 1st character is a | b | c, all characters past that are valid. So, add 3 * 26^3 to the answer. Now, say 1st character is d. Then, we can have all characters from a to g (7) and last 2 characters can be anything. So, add 7 * 26^2 to the answer. Going on in this way, we get the answer as:
26^1 + 26^2 + 26^3 + (3 * 26^3) + (7 * 26^2) + (2 * 26^1) + (0) + 1
= 75791
OK. Now the second thing, which I think OP is actually asking (to print all strings before we get a match). Now, why I think this is unfeasible is because if we have input as zzzzz (5 characters long) we need to print 26^1 + 26^2 + 26^3 + 26^4 + 26^5 strings, which is 12356630. So, for this part, I assume max length of input string is 5 (And definitely no more) because for 6 character length string, we need to print ~321272406 strings --> NOT POSSIBLE.
So, a simple solution to this can be:
Create an array of size 27 as: arr[] = {'', 'a', 'b', ..., 'y', 'z'}. 1st character is null.
Write 5 (max string length) nested loops from 0 to 26 (inclusive) and add it to dummy string and print it. Something like.
for i from 0 to 26
String a = "" + arr[i]
for j from 0 to 26
String b = a + arr[j]
for k from 0 to 26
String c = b + arr[k]
for l from 0 to 26
String d = c + arr[l]
for m from 0 to 26
String e = d + arr[m]
print e
if(e equals input string)
break from all loops //use some flag, goto statement etc.
You asked for a more elegant solution, here's a simple function that converts integers into lowercase strings of characters allowing you to easily iterate through strings.
function toWord(val, first, out) {
if(first == 1)
out = String.fromCharCode(97 + val % 26) + out;
else
out = String.fromCharCode(97 + (val-1) % 26) + out;
if(val % 26 == 0 && first == 0) {
val -= 26;
}
else {
val -= val %26;
}
val = val / 26;
if(val != 0)
return toWord(val, 0, out);
else
return out;
}
It's by no means perfect, but it's short and simple. When calling the function set val to be the integer you want to convert, first as 1, and out as "".
For example the following will apply yourFunction to the first 10,000 lowercase strings
for(int i=0; i<10000; i++) {
youFunction(toWord(i, 1, ""));
}
So you need to always start incrementing from a? Since they are char values you can easily do this with the following construct:
Note that this is a java solution :)
public static char[] increment(char[] arr, int pos) {
// get current position char
char currentChar = arr[pos];
// increment it by one
currentChar++;
// if it is at the end of it's range
boolean overflow = false;
if (currentChar > 'Z') {
overflow = true;
currentChar = 'A';
}
// always update current char
arr[pos] = currentChar;
if (overflow) {
if (pos == 0) {
// resize array and add new character
char[] newArr = new char[arr.length + 1];
System.arraycopy(arr, 0, newArr, 0, arr.length);
newArr[arr.length] = 'A';
arr = newArr;
} else {
// overflowed, increment one position back
arr = increment(arr, pos - 1);
}
}
return arr;
}
public static void main(String[] args) {
final String stringToUse = "A";
final String stringToSearch = "ABCD";
char[] use = stringToUse.toCharArray();
for (;;) {
final String result = new String(use);
System.out.println("Candidate: " + result);
if (stringToSearch.equals(result)) {
System.out.println("Found!");
break;
}
use = increment(use, use.length - 1);
}
}