Javascript Last Letter getting cut off - javascript

Alright, so basically this code passes in a sentence into the function and the function needs to figure out which word is longest to return. Everything works great except that the very last letter keeps getting cut off. So what would be a good solution to this problem?
function LongestWord(sen) {
sen = sen.toLowerCase();
var build = "";
var arr = [];
var longest = 0;
for(var i = 0; i < sen.length;i++){
var cur = sen.charCodeAt(i);
console.log(sen.charCodeAt(i))
if(i == sen.length - 1){
arr.push(build);
}
if(sen.charAt(i) === " "){
arr.push(build);
build = "";
}
if(cur >= 97 && cur <= 122){
build += String.fromCharCode(cur);
}
}
console.log(arr);
for(var e = 0; e < arr.length - 1;e++){
if(arr[e].length > arr[e + 1].length){
longest = arr[e];
}
else{
longest = arr[e + 1];
}
}
return longest;
}
// keep this function call here
// to see how to enter arguments in JavaScript scroll down
console.log(LongestWord("Johnny ErsoL"));
It returns "Johnny", which is correct, but this is what the Array looks like at the end.
[ 'johnny', 'erso' ]

Here's my suggestion ?
function LongestWord(sen) {
return sen.split(/\b/).filter(function(item) {
return item.trim().length;
}).sort(function(a,b) {
return b.length - a.length;
});
}
split the sentence on word boundary, then trim off empty spaces, finally sort by length of each word and return the sorted array.

Try replacing
for(var i = 0; i < sen.length;i++){
var cur = sen.charCodeAt(i);
console.log(sen.charCodeAt(i))
if(i == sen.length - 1){
arr.push(build);
}
if(sen.charAt(i) === " "){
arr.push(build);
build = "";
}
if(cur >= 97 && cur <= 122){
build += String.fromCharCode(cur);
}
}
with
arr=sen.split();

You have a for loop with e <arr.length -1. I don't think you need the -1.

I know you already found your answer, however I just wanted to show you an alternative to what you are doing.
To simplify your code and also make it more understandable, but also flexible, it's usually a good idea to make sure that every function does only one thing, or has a single responsability.
In your code, your LongestWord function has the responsability to identify what is a word and to find out which one is the longest.
What you could have done is create a function that knows how to tokenize a sentence into words:
function forEachWordsIn(str, callback) {
var rx = /\b(\w+?)\b/g,
match;
while (match = rx.exec(str)) callback(match[0]);
}
Then use that words iterator function from the longestWord function, which makes this algorithm extremely trivial now:
function longestWord(sen) {
var longestWord = '';
forEachWordsIn(sen, function (word) {
if (word.length > longestWord.length) longestWord = word;
});
return longestWord;
}
Note: I renamed LongestWord to longestWord because making functions start with captital letters is a well known standard to identify constructor functions.

Related

For loop if statement stopping on first case

The challenge:
Write a function that takes in a string of one or more words, and returns the same string, but with all five or more letter words reversed (Just like the name of this Kata). Strings passed in will consist of only letters and spaces. Spaces will be included only when more than one word is present.
Example:
spinWords( "Hey fellow warriors" ) => returns "Hey wollef sroirraw"
At the moment I have this
function spinWords(str) {
var splitArray = str.split(' ')
for (var i = 0; i < splitArray.length; i++) {
if (splitArray[i].length > 5) {
var long = splitArray[i].split('').reverse('').join('')
return long
i++
} else {
var short = splitArray[i]
return short
i++
}
}
}
As I said in the title, this is working properly but will only return the first element in the array as reversed or not. Can anyone smarter than me please explain why the loop is not looping?
Thank you for your time.
Return ends the function.
Another approach.
const spinWords = words =>
words
.split(" ")
.map(word => (word.length >= 5 ? [...word].reverse().join("") : word))
.join(" ");
console.log(spinWords("Hey fellow warriors"));
you are almost there..
using for loop, you do not want to do another i++..
you said that it would be 5 or more.. so it should be >=5
return terminates the for loop, so use it last..
the modified function can look like this:
function spinWords(str){
var splitArray = str.split(' ');
var spinnedWords = '';
for (var i = 0; i < splitArray.length; i++) {
if (splitArray[i].length >= 5) {
var long = splitArray[i].split('').reverse('').join('');
spinnedWords = spinnedWords.concat(' ' + long);
}
else {
var short = splitArray[i]
spinnedWords = spinnedWords.concat(' ' + short);
}
}
return spinnedWords.trim();
}
Three things must be changed for this code to work properly.
First
The return statement will finish the entire function execution. So it should be placed at the end of the body when no more code will be executed.
Second
You can switch the values you are iterating over and then return the same array with the inverted operation of the first line (.join(' ')).
Third
The for loop already increment the index counter at the end of each iteration if you defined it in the parameters. You don't need i++ inside the loop body.
function spinWords(str) {
var splitArray = str.split(' ');
for (var i = 0; i < splitArray.length; i++) {
if (splitArray[i].length >= 5) {
var long = splitArray[i]
.split('')
.reverse('')
.join('');
splitArray[i] = long;
}
}
return splitArray.join(' ')
}
EDIT: There's no need for else statement
EDIT2: I forgot the third change needed
function spinWords(str) {
return str
.split(" ")
.map(word => word.length >= 5 ? word.split("").reverse().join("") : word)
.join(" ");
}
console.log( spinWords("Hey fellow warriors") );
This is my answer after your tips:
function spinWords(str){
var splitArray = str.split(' ')
var finalArray = []
for (var i = 0; i < splitArray.length; i++) {
if (splitArray[i].length >= 5) {
var long = splitArray[i].split('').reverse('').join('')
finalArray.push(long)
}
else {
var short = splitArray[i]
finalArray.push(short)
}
}
return finalArray.join(' ')
}

Word Break algorithm

I'm trying to implement the "Word Break" algorithm.
Problem:
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
Note:
The same word in the dictionary may be reused multiple times in the segmentation.
You may assume the dictionary does not contain duplicate words.
Example:
Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".
My solution:
var wordBreak = function(s, wordDict) {
if(!wordDict || wordDict.length === 0)
return false;
while(wordDict.length > 0 || s.length > 0) {
const word = wordDict.shift();
const index = s.indexOf(word);
if(index === -1) {
return false;
}
s = s.substring(0, index) + s.substring(index+word.length, s.length);
}
return s.length === 0 && wordDict.length === 0 ? true : false;
};
It works for the example (input) above. However it fails for the input below.
Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
Note that you are allowed to reuse a dictionary word.
How can I keep track of words that I already eliminate and check it at the end. This input above, the remaining s string contains "apple" which is in the word dictionary, so the output should be true.
Thanks
A simple Javascript solution.
This loops through the wordDict array and checks if each word exist in the str. If it doesn't that is when the indexOf the word return -1, the function returns false. However, if the words in the wordDict array are in the string, it returns true at the end of the for loop.
const wordBreak =(str, wordDict)=>{
if (!wordDict || wordDict.length === 0) return false
for(let i=0; I<wordDict.length; i++){
const dictIndex = str.indexOf(wordDict[i])
if(dictIndex === -1){
return false
}
}
return true
}
This is an interesting problem I met two years ago in a different context, i.e., query tokenization. In my case, the number of words in the dictionary was in the order of several million, therefore a recursive approach looking each time for a different word of the dictionary was not practicable. Furthermore, I needed to apply dynamic programming to solve the task for strict efficiency reasons.
First of all, I suggest you to use the AhoCorasick algorithm to find the words within your search string. The algorithm looks for an arbitrary number of patterns in a string in linear time in the length of the string regardless of the number of patterns to find (no more number of words times length of the string operation, indeed each find of a word in a string needs to scan the entire string..).
Luckily, I found a javascript implementation of the algorithm here.
Using the code linked above and dynamic programming to track the words appearing in your string, I wrote the following javascript solution:
function wordBreak(s, wordDict) {
const len = s.length;
const memoization_array_words = new Array(len).fill(null);
const memoization_array_scores = new Array(len).fill(0);
const wordScores = {};
wordDict.forEach(function(word) {
wordScores[word] = 1
});
automata = new AhoCorasick(wordDict);
results = automata.search(s);
results.forEach(function(result) {
// result[0] contains the end position
// result[1] contains the list of words ending in that position
const end_pos = result[0];
result[1].forEach(function(word) {
const prev_end_pos = end_pos - word.length;
const prev_score = (prev_end_pos == -1) ? 0 : memoization_array_scores[prev_end_pos];
const score = prev_score + wordScores[word];
if (score > memoization_array_scores[end_pos]) {
memoization_array_words[end_pos] = word;
memoization_array_scores[end_pos] = score;
}
});
});
if (memoization_array_words[len-1] == null) {
return false;
}
solution = []
var pos_to_keep = len - 1;
while (pos_to_keep >= 0) {
const word = memoization_array_words[pos_to_keep];
solution.push(word);
pos_to_keep -= word.length;
}
return solution.reverse()
}
where memoization_array_words and memoization_array_scores are filled left to right when we meet a word occurring after a previous one or at the beginning of the string s. The code should be autoesplicative, but if you need any explanation write me a comment, please.
As a plus, I associated a score to each word (here is 1 for simplicity) that allows you to distinguish between the different solutions. For instance, if you associate to each word an importance score, you will end up with the tokenization with the greatest score. In the code above, the tokenization with the highest number of words.
Extended version: I testing over the wordDict with some if there is one of the worde that beginns at the test-string (indexOf==0). If so I shorten the string about the length of the word and call the function recursivly with the shortened string. Otherwise the string is not splitable and I return false. I go this way on till an error occurs or the length of the string is 0 and I win because everything goes allright.
Remark: The error when the WordBreak is not clearly like with s= "cars" wordDict = ["car","ca","rs"] is now fixed. For this I calling in the some-methode the algorithm recursivly. So if one way stops before ending I go backwards and search for alternatives till I found one or there is no possibility left.
Remarks to; array.some
In an array.forEach there can't used a break without using some ugly tricks (like try...catch and throwing an error), so I could use the classic variant of the for-loop. But there exists the array.some method this loops like a forEach-loop but there had only one of the elements to be return true so the result is true.
Example:
const array = [1, 2, 3, 4, 5];
// checks whether an element is even
const even = (element) => element % 2 === 0;
console.log(array.some(even));
Here is the code of the working algorithm.
var wordBreak = function(s, wordDict) {
if (!wordDict || wordDict.length === 0) return false;
while (s.length > 0) {
let test = wordDict.some( (word,index) => {
if (s.indexOf(word)===0) {
s_new = s.substr(word.length);
return wordBreak(s_new, wordDict);
}
});
if (!test ) return false;
s=s_new;
}
if (s.length === 0) return true;
}
s = "leetcode"; wordDict = ["leet", "code"];
console.log(wordBreak(s, wordDict));
s = "applepenapple"; wordDict = ["apple", "pen"];
console.log(wordBreak(s, wordDict));
s= "cars"; wordDict = ["car","ca","rs"];
console.log(wordBreak(s, wordDict));
function wordBreak(dict, str){
if (!str){
return true;
}
for (const word of dict){
if (str.startsWith(word)){
return wordBreak(dict, str.substring(word.length, str.length))
}
}
return false;
}
You could also probably optimize the loop over dict by pre-sorting the array and using binary search, but hopefully this gets the point across.
If you'd be looking for a Dynamic Programming solution, we'd use an array for recording, and then we'd loop through and keep track of the word.
This'll pass through in JavaScript:
const wordBreak = function(s, wordDict) {
const len = s.length
const dp = new Array(len + 1).fill(false)
dp[0] = true
for (let i = 1; i < len + 1; i++) {
for (let j = 0; j < i; j++) {
if (dp[j] === true && wordDict.includes(s.slice(j, i))) {
dp[i] = true
break
}
}
}
return dp[s.length]
}
In Python, we would have used a list (which is similar to an array of JavaScript) with the same size as our string:
class Solution:
def wordBreak(self, s, words):
dp = [False] * len(s)
for i in range(len(s)):
for word in words:
k = i - len(word)
if word == s[k + 1:i + 1] and (dp[k] or k == -1):
dp[i] = True
return dp[-1]
Similarly in Java, we'd have used a boolean[]:
public final class Solution {
public static final boolean wordBreak(
String s,
List<String> words
) {
if (s == null || s.length() == 0) {
return false;
}
final int len = s.length();
boolean[] dp = new boolean[len];
for (int i = 0; i < len; i++) {
for (int j = 0; j <= i; j++) {
final String sub = s.substring(j, i + 1);
if (words.contains(sub) && (j == 0 || dp[j - 1])) {
dp[i] = true;
break;
}
}
}
return dp[len - 1];
}
}
Here is LeetCode's DP solution:
public class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> wordDictSet=new HashSet(wordDict);
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for (int i = 1; i <= s.length(); i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && wordDictSet.contains(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}
References
For additional details, please see the Discussion Board which you can find plenty of well-explained accepted solutions in there, with a variety of languages including efficient algorithms and asymptotic time/space complexity analysis1, 2.

JS: Reversing a string using nested loop does not work

I have written a function called reverseStr that takes in a string as a parameter and returns the string but with the characters in reverse.
For example: reverseStr('bootcamp'); => 'pmactoob'
Following is my program:
function reverseStr(str)
{
var splitStr = str.split("");
console.log(splitStr);
var reverseString = [];
for(var i = 0; i <= splitStr.length -1 ; i++)
{
for(var j = splitStr.length - 1; j >= 0; j--)
{
reverseString[i] = splitStr[j]
}
}
return reverseString.toString().replace(/[&\/\\#,+()$~%.'":*?<>{}]/g, '');
}
If I run the function reverseStr("bootcamp") it returns bbbbbbbb.
Does anyone see a problem with the code?
Note: I DONOT WANT TO USE REVERSE() BUILT-IN FUNCTION
However, I found success with the following code but still need an answer to my initial question
function reverseStr(str)
{
var splitStr = str.split("");
reverseStr = "";
for(var i = splitStr.length - 1; i >= 0 ; i = i - 1)
{
reverseStr += splitStr[i];
}
return reverseStr;
}
You don't need to double-iterate through the characters, i.e., do not need to nest for loops. Iterate once and grab the chars in reverse order, like this:
function reverseStr(str)
{
var splitStr = str.split("");
console.log(splitStr);
var reverseString = [];
for(var i = 0, j=splitStr.length-1; i <= splitStr.length -1 ; i++, j--)
{
reverseString[i] = splitStr[j]
}
return reverseString.toString().replace(/[&\/\\#,+()$~%.'":*?<>{}]/g, '');
}
You can see that here the loop goes on for as long as i <= splitStr.length -1,ie, length of the string. This is sufficient to get the mirroring character (i versus Array.length-i).
Here is a working snippet to demo:
var reverseStr = function(str) {
let result = String();
for(let i = str.length-1; i >= 0; i--) {
result += str.charAt(i);
}
return result.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g, '');
}
$('button').click(function() {
$('.result').text(reverseStr($('#str').val()));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="str">
<button>Reverse it</button>
<div class="result"></div>
Perhaps a more elegant way to achieve the same (apart from Array.prototype.reverse()) would be to use String.prototype.chatAt(). This would avoid two conversions to and from an array, and also save you one variable. Granted, the code is much shorter and more obvious in what it is doing.
var reverseStr = function(str) {
let result = String(); // An empty string to store the result
for(let i = str.length-1; i >= 0; i--) { // Iterate backwards thru the chars and add to the result string
result += str.charAt(i);
}
return result.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g, ''); // Original return method of the author
}
$('button').click(function() {
$('.result').text(reverseStr($('#str').val()));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="str">
<button>Reverse it</button>
<div class="result"></div>
The problem is that your nested for loop runs its whole course before it returns to the outer for loop. So, it just repeats one character the amount of times equal to the length. Instead of having another for loop, just add a simple counter for j like j++ inside your outer for loop and use that value with the i value.
To the original poster, consider this:
If you know the length of the original string, you therefore know the offset of that last position within the original string.
Iterate through the original string in reverse order, appending the current position's value to a new string. The new string would be the reverse of the original.
Aydin's example is essentially correct. Here's my very similar version, with comments:
function reverseString(inputString) {
// create a new empty string
var newString = "";
// iterate through the characters of the string in reverse order,
// appending to the new string
for (var i = inputString.length - 1; i >= 0; i--) {
newString += inputString[i];
}
return newString;
}
console.log(reverseString('bootcamp'));

Find smallest substring containing a given set of letters in a larger string

Say you have the following string:
FJKAUNOJDCUTCRHBYDLXKEODVBWTYPTSHASQQFCPRMLDXIJMYPVOHBDUGSMBLMVUMMZYHULSUIZIMZTICQORLNTOVKVAMQTKHVRIFMNTSLYGHEHFAHWWATLYAPEXTHEPKJUGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNT
LDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFY
FFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQ
XBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGR
AMELUTEPYILBIUOCKKUUBJROQFTXMZRLXBAMHSDTEKRRIKZUFNLGTQAEUINMBPYTWXULQNIIRXHHGQDPENXAJNWXULFBNKBRINUMTRBFWBYVNKNKDFR
I'm trying to find the smallest substring containing the letters ABCDA.
I tried a regex approach.
console.log(str.match(/[A].*?[B].*?[C].*?[D].*?[A]/gm).sort((a, b) => a.length - b.length)[0]);
This works, but it only find strings where ABCDA appear (in that order). Meaning it won't find substring where the letters appear in a order like this: BCDAA
I'm trying to change my regex to account for this. How would I do that without using | and type out all the different cases?
You can't.
Let's consider a special case: Assume the letters you are looking for are A, A, and B. At some point in your regexp there will certainly be a B. However, the parts to the left and to the right of the B are independent of each other, so you cannot refer from one to the other. How many As are matched in the subexpression to the right of the B depends on the number of As being already matched in the left part. This is not possible with regular expressions, so you will have to unfold all the different orders, which can be many!
Another popular example that illustrates the problem is to match opening brackets with closing brackets. It's not possible to write a regular expression asserting that in a given string a sequence of opening brackets is followed by a sequence of closing brackets of the same length. The reason for this is that to count the brackets you would need a stack machine in contrast to a finite state machine but regular expressions are limited to patterns that can be matched using FSMs.
This algorithm doesn't use a regex, but found both solutions as well.
var haystack = 'FJKAUNOJDCUTCRHBYDLXKEODVBWTYPTSHASQQFCPRMLDXIJMYPVOHBDUGSMBLMVUMMZYHULSUIZIMZTICQORLNTOVKVAMQTKHVRIFMNTSLYGHEHFAHWWATLYAPEXTHEPKJUGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNTLDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFYFFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQXBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGRAMELUTEPYILBIUOCKKUUBJROQFTXMZRLXBAMHSDTEKRRIKZUFNLGTQAEUINMBPYTWXULQNIIRXHHGQDPENXAJNWXULFBNKBRINUMTRBFWBYVNKNKDFR';
var needle = 'ABCDA'; // the order of letters doesn't matter
var letters = {};
needle.split('').forEach(function(ch) {
letters[ch] = letters[ch] || 0;
letters[ch]++;
});
var shortestSubstringLength = haystack.length;
var shortestSubstrings = []; // storage for found substrings
var startingPos = 0;
var length;
var currentPos;
var notFound;
var letterKeys = Object.keys(letters); // unique leters
do {
lettersLeft = JSON.parse(JSON.stringify(letters)); // copy letters count object
notFound = false;
posStart = haystack.length;
posEnd = 0;
letterKeys.forEach(function(ch) {
currentPos = startingPos;
while (!notFound && lettersLeft[ch] > 0) {
currentPos = haystack.indexOf(ch, currentPos);
if (currentPos >= 0) {
lettersLeft[ch]--;
posStart = Math.min(currentPos, posStart);
posEnd = Math.max(currentPos, posEnd);
currentPos++;
} else {
notFound = true;
}
}
});
if (!notFound) {
length = posEnd - posStart + 1;
startingPos = posStart + 1; // starting position for next iteration
}
if (!notFound && length === shortestSubstringLength) {
shortestSubstrings.push(haystack.substr(posStart, length));
}
if (!notFound && length < shortestSubstringLength) {
shortestSubstrings = [haystack.substr(posStart, length)];
shortestSubstringLength = length;
}
} while (!notFound);
console.log(shortestSubstrings);
Maybe not as clear as using regex could be (well, for me regex are never really clear :D ) you can use brute force (not so brute)
Create an index of "valid" points of your string (those with the letters you want) and iterate with a double loop over it getting substrings containing at least 5 of those points, checking that they are valid solutions. Maybe not the most efficient way, but easy to implement, to understand, and probably to optimize.
var haystack="UGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNTLDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFYFFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQXBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGR";
var needle="ABCD";
var size=haystack.length;
var candidate_substring="";
var minimal_length=size;
var solutions=new Array();
var points=Array();
for(var i=0;i<size;i++){
if(needle.indexOf(haystack[i])>-1) points.push(i);
}
var limit_i= points.length-4;
var limit_k= points.length;
for (var i=0;i<limit_i;i++){
for(var k=i;k<limit_k;k++){
if(points[k]-points[i]+1<=minimal_length){
candidate_substring=haystack.substr(points[i],points[k]-points[i]+1);
if(is_valid(candidate_substring)){
solutions.push(candidate_substring);
if(candidate_substring.length < minimal_length) minimal_length=candidate_substring.length;
}
}
}
}
document.write('<p>Solution length:'+minimal_length+'<p>');
for(var i=0;i<solutions.length;i++){
if(solutions[i].length<=minimal_length) document.write('<p>Solution:'+solutions[i]+'<p>');
}
function is_valid(candidate_substring){
//verify we've got all characters
for(var j=0;j<candidate_substring.length;j++){
if(candidate_substring.indexOf(needle.charAt(j))<0) return false;
}
//...and verify we have two "A"
if(candidate_substring.indexOf("A")==candidate_substring.lastIndexOf("A")) return false;
return true;
}
Just had this problem in an interview as a coding assignment and came up with another solution, (it's not as optimal as the one above but maybe it's easier to understand).
function MinWindowSubstring(strArr) {
const N = strArr[0];
const K = strArr[1];
const letters = {};
K.split('').forEach( (character) => {
letters[character] = letters[character] ? letters[character] + 1 : 1;
});
let possibleSequencesList = [];
const letterKeys = Object.keys(letters);
for(let i=0; i< N.length; i++) {
const char = N[i];
if (new String(letterKeys).indexOf(char) !== -1) {
// found a character in the string
// update all previus sequences
possibleSequencesList.forEach((seq) => {
if(!seq.sequenceComplete) {
seq[char] = seq[char]-1;
seq.lastIndex = i;
// check if sequence is complete
var sequenceComplete = true;
letterKeys.forEach( (letter) => {
if(seq[letter] > 0) {
sequenceComplete = false;
}
});
seq.sequenceComplete = sequenceComplete
}
})
// create a new sequence starting from it
const newSeq = {
startPoint: i,
lastIndex: i,
sequenceComplete: false,
...letters
}
newSeq[char] = newSeq[char]-1;
possibleSequencesList.push(newSeq);
}
}
// cleanup sequences
let sequencesList = possibleSequencesList.filter(sequence => sequence.sequenceComplete);
let output = [];
let minLength = N.length;
// find the smalles one
sequencesList.forEach( seq => {
if( (seq.lastIndex - seq.startPoint) < minLength) {
minLength = seq.lastIndex - seq.startPoint;
output = N.substring(seq.startPoint, seq.lastIndex + 1);
}
})
return output;
}

if else statement in while loop, where if else statement determines when while loop stops

var userInput = prompt("type something with or without double spacing");
var errorSpaces = [];
var maxSpaceCount = [];
var doneOnce = 0;
var done = 0;
var size;
var tempArray = [0, 0];
while (done === 0) {
if (doneOnce === 0) {
for (var i = 0; i<size; i++) {
size = userInput.length - 1;
if (userInput.substr(i, 2) == " ") {
userInput.replace(userInput.substring(i, j), userInput[i]);
errorSpaces.push(0);
}
}
doneOnce = 1;
maxSpaceCount.push(0);
} else if (doneOnce === 1 && tempArray.length != 1) {
for (var i = 0; i<size; i++) {
tempArray = [0];
size = userInput.length - 1;
if (userInput.substr(i, 2) == " ") {
userInput.replace(userInput.substring(i, j), userInput[i]);
tempArray.push(0);
}
doneOnce = 2;
}
maxSpaceCount.push(0);
} else {
done = 1;
}
}
alert("done");
This loops at the second for loop rather than finishing. I know it probably isn't the best way to do it, but how could I make the 'else if' work so that when there are no more double spaces, it will go to the final else?
I am trying to eliminate any multiple spaces by iteratively replacing double spaces with single spaces, then re-reading to replace further double (previously triple) spaces, etc.
Way , way too much code if your goal is to replace any double (or more spaces) with a single space
try regex
var userInput = prompt("type something with or without double spacing");
userInput = userInput.replace(/\s{2,}/g, ' ');
alert("done");
although not quite sure what you are trying to do with tempArray as it doesn't seem to make sense.
EDIT
There appears to be some indication that there is a requirement to count how many occurrences of 2 or more spaces, so using the below will give you the count. The reason for the || bit is because if none are found, it will return null, || [] will change the null to empty array, so the length of it will be zero. Thanks to #RobG
var countOfMultipleSpaces = (userInput.match(/\s{2,}/g) || []).length;
I'm sure it goes without saying that you have to do this before you replace them all
Is this what you want?
"type something with or without double spacing".replace(/\s{2,}/g, ' ');
//"type something with or without double spacing"

Categories