First of all, from what I understand from the question, the result for Input: "aaagtaayuhiejjgiiiouaae" should be aaaaaeiiiou, as @PhamTrung asked in the comments, but did not receive an answer.
Since this is a job interview, I would start with the first thing that comes to mind, namely, from the brute force of the decision from this
function a(string, prefix='') { if(!string.length){ return prefix } if(!/[aeiou]/.test(string[0])){ return a(string.substr(1), prefix) } const option1 = a(string.substr(1), prefix) const option2 = a(string.substr(1), prefix+string[0]) const validateRegex = /^a+e+i+o+u+$/ const isValidOption1 = validateRegex.test(option1) const isValidOption2 = validateRegex.test(option2) if(isValidOption1 && isValidOption2){ if(option1.length > option2.length) { return option1 } return option2 } if(isValidOption1) { return option1 } if(isValidOption2) { return option2 } return null } const input = 'aaagtaayuhiejjhgiiiouaae' console.log(a(input))
This one has a terrible runtime, we try to use all possible substring that contains only vowels, than we discard those that do not have the required shape (a + e + i + o + u +), but select only the largest of them. If I'm not mistaken, this has the worst case of Σ (n picks i), which is O (n ^ n) - well, in the worst case, there would be a stackOverflow exception for a sufficiently large n, in which case we would have to override this loop instead of recursion. In this case, we could still get an exception from memory, and in this case we would have no options but to improve our algorithm. It is fair to assume that if the input was large enough to receive an exception from memory, our code would also be slow enough not to be a reasonable solution to the problem. I’m just arguing about all this because this is what the interviewer might want to see that you know, which means that you know enough CS 101.
After that, the interviewer asked if I could improve performance. This is a runtime solution of O (n).
const input = 'aeiouaaaeeeiiiooouuu' let curr = { "1": {price: -1} } const nodes = [] const voewels = '1aeiou' const getPrevLetter = (node) => voewels[voewels.indexOf(node.letter) -1] let resultNode function setUpNodeByCurrent(node, letter){ node.price = curr[letter].price + 1 node.previous = curr[letter] } function setBestResultIfNeeded(node){ if(node.letter !== 'u') { return } if(!resultNode || resultNode.price < node.price) { resultNode = node return } } function setCurrent(letter){ const node = { letter, price: 0 } const prevLetter = getPrevLetter(node) if(!prevLetter || !curr[prevLetter]){
This can be seen as a simplification of the problem with a long trajectory , basically you run through the line and every a indicates the next occurrence of a and the next occurrence of e . e indicates the next e and the next i , etc. You should have a start node pointing to each a and end node visibility that all u events point to. Now you need the longest path from the beginning of the node to the end of the node, which is O (| V | + | E |), now | V | <= n and | E | <= 2n since each node on your graph has no more than 2 vertices, so the total runtime is O (n). I simplified the code for constructing the result when it goes on plotting, basically I already calculate the cost on the go, so when I finished building a graph similar to what I described, I already know what the result is.
Please note that this solution is based on the assumption that the input string is one that necessarily has a solution built into it. If the input string is unsolvable (it does not contain the aeiou sequence), then this case should be handled correctly, in fact, it is trivial to add code that processes this. The first solution will return null in this case (if I'm not mistaken)
Hope this helps.