Next Greatest Alphabet in an Array [Using Binary Search Algorithm] - javascript

I am trying to solve a question on Leetcode
Find Smallest Letter Greater Than Target
Given a characters array letters that is sorted in non-decreasing order and a character target, return the smallest character in the array that is larger than target.
Note that the letters wrap around.
For example, if target == 'z' and letters == ['a', 'b'], the answer is 'a'.
Example 1:
Input: letters = ["c","f","j"], target = "a"
Output: "c"
Example 2:
Input: letters = ["c","f","j"], target = "c"
Output: "f"
Example 3:
Input: letters = ["c","f","j"], target = "d"
Output: "f"
Constraints:
2 <= letters.length <= 104
letters[i] is a lowercase English letter.
letters is sorted in non-decreasing order.
letters contains at least two different characters.
target is a lowercase English letter.
My solution until now looks something like this:
function nextGreatestAlphabet(letters, target){
let left = 0;
let right = letters.length - 1;
let res = -1;
while(left <= right) {
let mid = Math.floor(left + (right - left) / 2);
if (letters[mid] == target ) { console.log(1)
res = letters[mid];
left = mid + 1;
}
if(letters[mid] > target){ console.log(2)
right = mid -1;
res = letters[mid];
}
if(letters[mid] < target ){ console.log(3)
left = mid + 1;
}
}
return res;
}
console.log(nextGreatestAlphabet(["c","f","j"],"c")); //c
But the correct result should be "f" as c is already present here and next greatest is f

You should avoid setting res during the search, and certainly not set it when letters[mid] == target, as it is sure that is not the correct answer. Instead you should get the result after the loop has exited.
This then also means you don't need a separate case for letters[mid] == target, as the rest of the action is exactly what is done for letters[mid] < target. So you can make this a quite simple if (letters[mid] > target) ... else ... structure.
After the loop has exited, the left index points to the desired character. Then you need to deal with the boundary case where that index points beyond the array and map it to 0. This you can do with the remainder operator:
NB: I use here the name of the function as required by the LeetCode challenge:
function nextGreatestLetter(letters, target) {
let left = 0;
let right = letters.length - 1;
while(left <= right) {
let mid = Math.floor(left + (right - left) / 2);
if(letters[mid] > target) {
right = mid -1;
} else {
left = mid + 1;
}
}
return letters[left % letters.length];
}
Personally, I prefer working with right being the index just after the range that is under consideration, and for deriving mid you can use the >> operator (Array sizes are limited in this challenge, so that is a safe operation):
function nextGreatestLetter(letters, target) {
let left = 0;
let right = letters.length;
while (left < right) {
let mid = (left + right) >> 1;
if (letters[mid] > target) {
right = mid;
} else {
left = mid + 1;
}
}
return letters[left % letters.length];
}

I think that your
if (letters[mid] == target ) { console.log(1)
res = letters[mid];
left = mid + 1;
}
sets the result to the target but you actually want to have the next one, so it should be sth. like
res=letter[mid+1].
if
mid==letters.length - 1
holds then it is already the right end. then you can just put
res = letter[0];

Related

Break line when string reaches X characters

I created this function below to calculate when a string (name) reaches more than 35 characters then it break a line.
function getName(name) {
let newName = name
for (let i = 0; i < name.length; i += 1) {
if (i % 35 === 0) {
newName = `${newName.substr(0, i)}\n${newName.substr(i)}`
}
}
return newName
}
console.log(getName("Please note that popular names below"))
Eg output:
Name = "Please note that popular names below"
function output:
Please note that popular names bel
ow
What I want is to instead of break a line of these two last characters "ow", I put "below" in the next line.
If you want to break a string to certain length without cutting off any word, following code can help you:
const name = "YOUR_STRING";
const maxLen = 35; // you can play with this number to get desired result.
let result = name.substr(0, maxLen);
result = result.substr(0, Math.min(result.length, result.lastIndexOf(" ")));
So you want to find the last space within the first 35 characters and break the line there, right?
You can first slice the first 35 characters and then use regex to find the last space.
To slice the first 35 characters, use name.substr(0,35). This returns a new string with the first 35 characters.
A regular expression that matches the last space is e.g. / (?!.* )/, so you can do replace(/ (?!.* )/, x) where x is whatever you want (new line, in this case).
You can I guess keep track of the last white space index, and then when reaching 35, break the line at the white space index
function getName(name) {
let newName = name
let wsIndex = 0
for (let i = 0; i < name.length; i += 1) {
if (name.charAt(i) == ' ') wsIndex = i;
if (i % 35 === 0) {
newName = `${newName.substr(0, wsIndex)}\n${newName.substr(wsIndex+1)}`
}
}
return newName
}
I came up with a simple solution, you simply have to look if the 35th character is a space(' '). If yes simply cut but if it isn't go back in the string until you hit a space, and cut there. My solution puts a line break if there are no spaces in the first 35 characters. Btw your code did copy the space to the next line, then you would have a small offset at every line.
The code:
function getName(name) {
let newName = name;
// offsets i to match the current string
let offset = 0;
for (let i = 1; i < name.length; i += 1) {
if (i % 35 === 0) {
if(newName.charAt(i) === ' ') {
// Your own code with the offset
newName = newName = `${newName.substr(0, i - offset)}\n${newName.substr(i - offset + 1)}`;
} else {
// looking back in the string until there is a space or the string "ends"
while(newName.charAt(i - offset) !== ' ' && offset <= 35) {
offset++;
}
// Only set newName if a space was found in the last 35
if(i - offset > 0) {
newName = `${newName.substr(0, i - offset)}\n${newName.substr(i - offset + 1)}`;
}
}
}
}
return newName;
}
console.log(getName("Please note that popular names below"));
JsFiddle
I tried to explain in the comments what i did and why.

Compare two sentences word by word and return the number of word matches considering different word forms

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];
}

Grouping Numbers JS algorithm

I was trying to solve a problem:
Problem:
Given an array of Positive repetitive numbers. Output
should give an array with odds sorted on the right and evens on the
left (no particular order)
Input : [4,1,2,3,4]
Output: [4,2,3,1]
Solve it In-place and without using extra space and O(N) runtime.
Code:
/*
* Algorithm is simple, have two pointers one on the left and another on the right.
* Note: We are sorting all evens on the left and odds on the right
* If you see even on left, move on else swap.
*/
function groupNumbers(intArr) {
if(intArr.length == 0 || intArr.length == 1){
return intArr;
}
for(let i=0, j =intArr.length-1; i<intArr.length; i++){
if(j>=i){ //elements should not overlap
let start = intArr[i];
let end = intArr[j];
if(start%2 == 0){ //Even
i++;
} else {
[start, end] = [end, start]; //swap
}
if(end%2 == 1){
j--;
} else {
[start, end] = [end, start]; //swap
}
} //if-ends
}//for-ends
return intArr;
}
I'm not sure where I'm going wrong. I'm missing something. I'm getting the same sorted array as output.
Condition: **SOLVE it INPLACE and without using extra space ** (Preferably in ONE iteration)
I'm not sure where I'm going wrong. I'm missing something. I'm getting the same sorted array as output.
several things:
let start = intArr[i];
let end = intArr[j];
...
[start, end] = [end, start];
this does indeed swap the values in the variables start and end, but not the indices in the Array.
Then you have two i++ in the same loop that increment the left pointer.
if(start%2 == 0){ //Even
i++;
} else {
[start, end] = [end, start]; //swap
}
here you swap the items when the left pointer points to an odd value, but there's no check that the right pointer also points to an even value. you might as well just swap two odd values here. Same for the right pointer.
const isEven = v => (v&1) === 0;
const isOdd = v => (v&1) === 1;
function groupNumbers(arr){
var left = 0, right = arr.length-1;
while(left < right){
//move the left pointer to find the next odd value on the left
while(left < right && isEven(arr[left])) ++left;
//move the right pointer to find the next even value on the right
while(left < right && isOdd(arr[right])) --right;
//checking that the two pointer didn't pass each other
if(left < right) {
console.log("swapping %i and %i", arr[left], arr[right]);
//at this point I know for sure that I have an odd value at the left pointer
//and an even value at the right pointer
//swap the items
var tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}
return arr;
}
[
[1,2,3,4],
[1,2,3,4,5,6,7,8,9,0],
[1,3,5,7],
[2,4,1,3],
[5,4,3,2,1],
].forEach(sequence => {
console.log("\ninput: " + sequence);
console.log("output: " + groupNumbers(sequence));
});
.as-console-wrapper{top:0;max-height:100%!important}
as suggested by #JaredSmith, the same thing just using a sort-function :)
function sortEvenLeftOddRight(a,b){
return (a&1) - (b&1);
//return (a&1) - (b&1) || a-b; //to additionally sort by value
}
[
[1,2,3,4],
[1,2,3,4,5,6,7,8,9,0],
[1,3,5,7],
[2,4,1,3],
[5,4,3,2,1],
].forEach(sequence => {
console.log("\ninput: " + sequence);
sequence.sort(sortEvenLeftOddRight);
console.log("output: " + sequence);
});
.as-console-wrapper{top:0;max-height:100%!important}
A very concise method to solve this would be to use reduce:
const out = arr.reduce((p, c) => {
// if the value is divisible by 2 add it
// to the start of the array, otherwise push it to the end
c % 2 === 0 ? p.unshift(c) : p.push(c)
return p;
}, []);
OUT
[4,2,4,1,3]
DEMO

JavaScript - Binary search hangs every time

I have a 2D array, something like the following:
[1.11, 23]
[2.22, 52]
[3.33, 61]
...
Where the array is ordered by the first value in each row.
I am trying to find a value within the array that is close to the search value - within a certain sensitivity. The way this is set up, and the value of the sensitivity, ensure only one possible match within the array.
The search value is the current x-pos of the mouse. The search is called on mousemove, and so is being called often.
Originally I had the following (using a start-to-end for loop):
for(var i = 0; i < arr.length; i++){
if(Math.abs(arr[i][0] - x) <= sensitivity){
hit = true;
break;
}
}
And it works like a charm. So far, I've only been using small data sets, so there is no apparent lag using this method. But, eventually I will be using much larger data sets, and so want to switch this to a Binary Search:
var a = 0;
var b = arr.length - 1;
var c = 0;
while(a < b){
c = Math.floor((a + b) / 2);
if(Math.abs(arr[c][0] - x) <= sensitivity){
hit = true;
break;
}else if(arr[c][0] < x){
a = c;
}else{
b = c;
}
}
This works well, for all of 2 seconds, and then it hangs to the point where I need to restart my browser. I've used binary searches plenty in the past, and cannot for the life of me figure out why this one isn't working properly.
EDIT 1
var sensitivity = (width / arr.length) / 2.001
The points in the array are equidistant, and so this sensitivity ensures that there is no ambiguous 1/2-way point in between two arr values. You are either in one or the other.
Values are created dynamically at page load, but look exactly like what I've mentioned above. The x-values have more significant figures, and the y values are all over the place, but there is no significant difference between the small sample I provided and the generated one.
EDIT 2
Printed a list that was dynamically created:
[111.19999999999999, 358.8733333333333]
[131.4181818181818, 408.01333333333326]
[151.63636363636363, 249.25333333333327]
[171.85454545454544, 261.01333333333326]
[192.07272727272726, 298.39333333333326]
[212.29090909090908, 254.2933333333333]
[232.5090909090909, 308.47333333333324]
[252.72727272727272, 331.1533333333333]
[272.94545454545454, 386.1733333333333]
[293.16363636363633, 384.9133333333333]
[313.3818181818182, 224.05333333333328]
[333.6, 284.53333333333325]
[353.81818181818187, 278.2333333333333]
[374.0363636363637, 391.63333333333327]
[394.25454545454556, 322.33333333333326]
[414.4727272727274, 300.9133333333333]
[434.69090909090926, 452.95333333333326]
[454.9090909090911, 327.7933333333333]
[475.12727272727295, 394.9933333333332]
[495.3454545454548, 451.27333333333326]
[515.5636363636366, 350.89333333333326]
[535.7818181818185, 308.47333333333324]
[556.0000000000003, 395.83333333333326]
[576.2181818181822, 341.23333333333323]
[596.436363636364, 371.47333333333324]
[616.6545454545459, 436.9933333333333]
[636.8727272727277, 280.7533333333333]
[657.0909090909096, 395.4133333333333]
[677.3090909090914, 433.21333333333325]
[697.5272727272733, 355.09333333333325]
[717.7454545454551, 333.2533333333333]
[737.963636363637, 255.55333333333328]
[758.1818181818188, 204.7333333333333]
[778.4000000000007, 199.69333333333327]
[798.6181818181825, 202.63333333333327]
[818.8363636363644, 253.87333333333328]
[839.0545454545462, 410.5333333333333]
[859.272727272728, 345.85333333333324]
[879.4909090909099, 305.11333333333323]
[899.7090909090917, 337.8733333333333]
[919.9272727272736, 351.3133333333333]
[940.1454545454554, 324.01333333333326]
[960.3636363636373, 331.57333333333327]
[980.5818181818191, 447.4933333333333]
[1000.800000000001, 432.3733333333333]
As you can see, it is ordered by the first value in each row, ascending.
SOLUTION
Changing the condition to
while(a < b)
and
var b = positions.length;
and
else if(arr[c][0] < x){
a = c + 1;
}
did the trick.
Your binary search seems to be a bit off: try this.
var arr = [[1,0],[3,0],[5,0]];
var lo = 0;
var hi = arr.length;
var x = 5;
var sensitivity = 0.1;
while (lo < hi) {
var c = Math.floor((lo + hi) / 2);
if (Math.abs(arr[c][0] - x) <= sensitivity) {
hit = true;
console.log("FOUND " + c);
break;
} else if (x > arr[c][0]) {
lo = c + 1;
} else {
hi = c;
}
}
This is meant as a general reference to anyone implementing binary search.
Let:
lo be the smallest index that may possibly contain your value,
hi be one more than the largest index that may contain your value
If these conventions are followed, then binary search is simply:
while (lo < hi) {
var mid = (lo + hi) / 2;
if (query == ary[mid]) {
// do stuff
else if (query < ary[mid]) {
// query is smaller than mid
// so query can be anywhere between lo and (mid - 1)
// the upper bound should be adjusted
hi = mid;
else {
// query can be anywhere between (mid + 1) and hi.
// adjust the lower bound
lo = mid + 1;
}
I don't know your exact situation, but here's a way the code could crash:
1) Start with an array with two X values. This array will have a length of 2, so a = 0, b = 1, c = 0.
2) a < b, so the while loop executes.
3) c = floor((a + b) / 2) = floor(0.5) = 0.
4) Assume the mouse is not within sensitivity of the first X value, so the first if branch does not hit.
5) Assume our X values are to the right of our mouse, so the second if branch enters. This sets a = c, or 0, which it already is.
6) Thus, we get an endless loop.

Permute string until it matches some input?

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);
}
}

Categories