I've been taking leetcode challenges and come across this interesting problem: 301. Remove Invalid Parentheses:
Given a string s that contains parentheses and letters, remove the minimum number of invalid parentheses to make the input string valid.
Return all the possible results. You may return the answer in any order.
Example 1:
Input: s = "()())()"
Output: ["(())()","()()()"]
Example 2:
Input: s = "(a)())()"
Output: ["(a())()","(a)()()"]
Example 3:
Input: s = ")("
Output: [""]
Constraints:
1 <= s.length <= 25
s consists of lowercase English letters and parentheses '(' and ')'.
There will be at most 20 parentheses in s.
I tried to solve it using recursion, but something is not right about my code, it's returning an empty array instead of the results.
Here is the code:
var removeInvalidParentheses = function(s) {
const validExpressions = [];
let minimumRemoved = 0;
function recurse(s, index, leftCount, rightCount, expression, removedCount) {
let possibleAnswer;
if (index === s.length) {
if (leftCount === rightCount) {
if (removedCount <= minimumRemoved) {
possibleAnswer = expression.join("");
if (removedCount < minimumRemoved) {
validExpressions.length = 0;
minimumRemoved = removedCount;
}
validExpressions.push(possibleAnswer);
}
}
} else {
let currentCharacter = s[index];
let length = expression.length;
if (currentCharacter !== '(' && currentCharacter !== ')') {
expression.push(currentCharacter);
recurse(s, index + 1, leftCount, rightCount, expression, removedCount);
expression.splice(length, 1);
} else {
recurse(s, index + 1, leftCount, rightCount, expression, removedCount + 1);
expression.push(currentCharacter);
if (currentCharacter == '(') {
recurse(s, index + 1, leftCount + 1, rightCount, expression, removedCount);
} else if (rightCount < leftCount) {
recurse(s, index + 1, leftCount, rightCount + 1, expression, removedCount);
}
expression.splice(length, 1);
}
}
}
recurse(s, 0, 0, 0, [], 0);
return validExpressions;
}
The problem is that you initialise minimumRemoved = 0 which makes it impossible to ever satisfy the condition if (removedCount <= minimumRemoved) { except if removeCount is 0. But if there is no match in that condition, nothing will be ever pushed to validExpressions.
You'll want to mimic the worst situation possible, so that any solution will be considered to be better than that. Therefore initialise removedCount as a high number, like s.length + 1 or -- why not -- Infinity.
There is a second issue in your code: it may collect duplicate values in validExpressions, so make your return statement like this:
return [...new Set(validExpressions)];
This will make it work. Now, this is not a very fast solution. You may want to try to find improvements.
The problems are correctly highlihted by trincot. Here is the final code:
let removeInvalidParentheses = function (s) {
const validExpressions = [];
let minimumRemoved = Number.MAX_SAFE_INTEGER;
function recurse(
s,
index,
leftCount,
rightCount,
expression,
removedCount
) {
let possibleAnswer;
// If we have reached the end of string.
if (index === s.length) {
// If the current expression is valid.
if (leftCount === rightCount) {
// If the current count of removed parentheses is <= the current minimum count
if (removedCount <= minimumRemoved) {
// Convert StringBuilder to a String. This is an expensive operation.
// So we only perform this when needed.
possibleAnswer = expression.join("");
// If the current count beats the overall minimum we have till now
if (removedCount < minimumRemoved) {
validExpressions.length = 0;
minimumRemoved = removedCount;
}
validExpressions.push(possibleAnswer);
}
}
} else {
let currentCharacter = s[index];
let length = expression.length;
// If the current character is neither an opening bracket nor a closing one,
// simply recurse further by adding it to the expression StringBuilder
if (currentCharacter !== "(" && currentCharacter !== ")") {
expression.push(currentCharacter);
recurse(
s,
index + 1,
leftCount,
rightCount,
expression,
removedCount
);
expression.splice(length, 1);
} else {
// Recursion where we delete the current character and move forward
recurse(
s,
index + 1,
leftCount,
rightCount,
expression,
removedCount + 1
);
expression.push(currentCharacter);
// If it's an opening parenthesis, consider it and recurse
if (currentCharacter == "(") {
recurse(
s,
index + 1,
leftCount + 1,
rightCount,
expression,
removedCount
);
} else if (rightCount < leftCount) {
// For a closing parenthesis, only recurse if right < left
recurse(
s,
index + 1,
leftCount,
rightCount + 1,
expression,
removedCount
);
}
// Undoing the append operation for other recursions.
expression.splice(length, 1);
}
}
}
recurse(s, 0, 0, 0, [], 0);
return Array.from(new Set(validExpressions));
};
Related
Im trying create my own substring method in JS
function substr(string, p1 = 0, p2 = string.length) {
if (string.length === 0 || p2 < 0 || p1 > p2) return '';
const p11 = p1 <= 0 ? 0 : p1
let p22 = p2 > string.length ? string.length : p2
const str = string.split('').reduce((acc, item, i) => {
if (i >= p11 && i < p22) {
console.log('i:', i)
return acc.concat(item)
}
return acc
}, '');
console.log('inside', str)
return str
}
when Im test this code I have an error
expect(substr('abba', 1, 0)).toEqual(''); // pass
expect(substr('abba', 0, 1)).toEqual('a'); // pass
expect(substr('abba', 0, 1)).toEqual('a'); // pass
expect(substr('abba', 1, 2)).toEqual('bb'); // failed
How I can fix my code to pass tests?
There is your code example:
1 function substr(string, p1 = 0, p2 = string.length) {
2 if (string.length === 0 || p2 < 0 || p1 > p2) return '';
3 const p11 = p1 <= 0 ? 0 : p1
4 let p22 = p2 > string.length ? string.length : p2
5 const str = string.split('').reduce((acc, item, i) => {
6 if (i >= p11 && i < p22) {
7 console.log('i:', i)
8 return acc.concat(item)
9 }
10 return acc
11 }, '');
12
13 console.log('inside', str)
14 return str
15 }
There are multiple mistakes in your code. You are still learning. That's no problem – everyone has started somehow. Let's name the mistakes:
On the line 2, there is p1 > p2. That does not make sense. You do not want to return empty string if the distance between string start and the substring start is greater that the maximum substring length.
Your variable names do not make sense. Try using names like startpos or length instead of names like p1 or p2.
And the third thing I found is that your substring function returns substring between positions p1 and p2, unlike the JS's builtin String.prototype.substr which returns substring between p1 and p1+p2.
Try it this way:
function substr(string, start, length = string.length) {
let outStr = '';
for (let pos = start;
pos < start + length && pos < string.length;
pos++) {
outStr += string[pos];
}
return outStr;
}
function expect(actual) {
return {
'toEqual': function (expected) {
if (actual == expected) {
document.body.innerText += `“${actual}” == “${expected}” (pass)\n`;
} else {
document.body.innerText += `“${actual}” /= “${expected}” (fail)\n`;
}
},
};
}
expect(substr('abba', 1, 0)).toEqual('');
expect(substr('abba', 0, 1)).toEqual('a');
expect(substr('abba', 0, 1)).toEqual('a');
expect(substr('abba', 1, 2)).toEqual('bb');
I think that using split and reduce for this is overkill. But if you want to use it, there is the code in the “pure functional style”, but it is inefficient, because it needs string.length steps instead of length steps to compute the result.
function substr(string, start, length = string.length) {
return string
.split('') // split between every character
.reduce((head, char, index) =>
(index >= start && head.length < length) // if we reached the substring start and there “is space” in the substring,
? head + char // append another character
: head, // else keep the substring
''); // start with empty substring
}
function expect(actual) {
return {
'toEqual': function (expected) {
if (actual == expected) {
document.body.innerText += `“${actual}” == “${expected}” (pass)\n`;
} else {
document.body.innerText += `“${actual}” /= “${expected}” (fail)\n`;
}
},
};
}
expect(substr('abba', 1, 0)).toEqual('');
expect(substr('abba', 0, 1)).toEqual('a');
expect(substr('abba', 0, 1)).toEqual('a');
expect(substr('abba', 1, 2)).toEqual('bb');
You should also have better test cases. Write test cases as if wou would teach someone to use your routines. The test cases can be important for understanding complex code. For example, take you favorite sentence and test the function using it. It will be easier to understand the test case the next time you see the code.
Straight out of CTCI, 8.14: Given a boolean expression consisting of the symbols 0 (false), 1 (true), & (AND), | (OR), and ^(XOR), and a desired boolean result value result, implement a function to count the number of ways of parenthesizing the expression such that it evaluates to result.
I'm attempting a brute force approach that calculates every single possible combo, if matches desired result, add it to an array(combos) and return that result length. It seems to work for most expressions, but not the 2nd example given. What do I seem to be missing?
function countEval(s, goalBool, combos = []) {
// on first call make s into array since theyre easier to work with
if (!(s instanceof Array)) {
// and turn 1s and 0s into their bool equivalent
s = s.split('').map((item) => {
if (item === '1') {
return true;
} else if (item === '0'){
return false;
} else {
return item;
}
});
}
if (s.length === 1 && s[0] === goalBool) {
combos.push(s[0]); // can be anything really
} else {
for (let i = 0; i < s.length - 2; i = i + 2) {
// splice out the next 3 items
const args = s.splice(i, 3);
// pass them to see what they evaluate too
const result = evalHelper(args[0], args[1], args[2]);
// splice that result back in s array
s.splice(i, 0, result);
// pass that array to recurse
countEval(s, goalBool, combos);
// remove said item that was just put in
s.splice(i, 1);
// and reset array for next iteration
s.splice(i, 0, ...args);
}
}
return combos.length;
}
function evalHelper(a, op, b) {
if (op === '|') {
return a || b;
} else if (op === '&') {
return a && b;
} else if (op === '^') {
return a !== b;
}
}
With the 2 examples given it works for the first one, but not the second...
console.log(countEval('1^0|0|1', false)); // 2, correct
console.log(countEval('0&0&0&1^1|0', true)); // 30, should be 10!?!?!
The Bug
Your program is not taking into account overlap.
Example
Consider your program when s = '1|1|1|1'.
In one of the depth-first search iterations, your algorithm will make the reduction s = (1|1)|1|1. Then in a deeper recursive level in the same search, your algorithm will make the reduction s = (1|1)|(1|1). Now s is fully reduced, so you increment the length of combos.
In a different depth-first search iteration, your algorithm will first make the reduction s = 1|1|(1|1). Then in a deeper recursive level in the same search, your algorithm will make the reduction s = (1|1)|(1|1). Now s is fully reduced, so you increment the length of combos.
Notice that for both cases, s was parenthesized the same way, thus your program does not take into account overlap.
A Better Solution
A lot of times, when a problem is asking the number of ways something can be done, this is usually a big indicator that dynamic programming could be a potential solution. The recurrence relation to this problem is a bit tricky.
We just need to pick a "principle" operator, then determine the number of ways the left and right side could evaluate to true or false. Then, based on the "principle" operator and the goal boolean, we can derive a formula for the number of ways the expression could evaluate to the goal boolean given that the operator we picked was the "principle" operator.
Code
function ways(expr, res, i, j, cache, spaces) {
if (i == j) {
return parseInt(expr[i]) == res ? 1 : 0;
} else if (!([i, j, res] in cache)) {
var ans = 0;
for (var k = i + 1; k < j; k += 2) {
var op = expr[k];
var leftTrue = ways(expr, 1, i, k - 1, cache);
var leftFalse = ways(expr, 0, i, k - 1, cache);
var rightTrue = ways(expr, 1, k + 1, j, cache);
var rightFalse = ways(expr, 0, k + 1, j, cache);
if (op == '|') {
if (res) {
ans += leftTrue * rightTrue + leftTrue * rightFalse + leftFalse * rightTrue;
} else {
ans += leftFalse * rightFalse;
}
} else if (op == '^') {
if (res) {
ans += leftTrue * rightFalse + leftFalse * rightTrue;
} else {
ans += leftTrue * rightTrue + leftFalse * rightFalse;
}
} else if (op == '&') {
if (res) {
ans += leftTrue * rightTrue;
} else {
ans += leftFalse * rightFalse + leftTrue * rightFalse + leftFalse * rightTrue;
}
}
}
cache[[i, j, res]] = ans;
}
return cache[[i, j, res]];
}
function countEval(expr, res) {
return ways(expr, res ? 1 : 0, 0, expr.length - 1, {});
}
Simply trying to find how many times a given character appears in a string but I can't solve it any other way then this simple for-loop. Is there a method that would solve this quicker or more eloquently other than using Regex?
function countCharacter(str, char) {
var count = 0;
for(var i = 0; i < str.length; i++){
if(str.charAt(i) === char)
count++;
}
return count;
}
There are many possible ways are available in the market.
I am adding a few of them.
Method 1:
str = "The man is as good as his word"
str.split('a')
output: (4) ["The m", "n is ", "s good ", "s his word"]
str.split('a').length - 1
output: 3
Method 2:
str = "The man is as good as his word"
str.split('').map( function(char,i)
{ if(char === 'a')
return i;
}
).filter(Boolean)
Output: (3) [5, 11, 19]
str.split('').map( function(char,i)
{ if(char === 'a')
return i;
}
).filter(Boolean).length
ouput: 3
Edit: As per comment we can also make use of filter().
str.split('').filter(function(char, i){
if(char == 'a'){
return i;
}
})
output: (3) ["a", "a", "a"]
str.split('').filter(function(char, i){
if(char == 'a'){
return i;
}
}).length
output: 3
----edited by adding more cases from answers----
there are several ways, you can use split/for/regex/reduce/indexOf like this:
function countCharacter_reduce(str, ch) {
return Array.prototype.reduce.call(str, (prev, cur) => cur === ch && ++prev && prev, 0);
}
function countCharacter_split(str, ch) {
return str.split(ch).length - 1;
}
function countCharacter_for(str, ch) {
for (var count = 0, ii = 0; ii < str.length; ii++) {
if (str[ii] === ch)
count++;
}
return count;
}
function countCharacter_regex(str, ch) {
return str.length - str.replace(new RegExp(ch, 'g'), '').length;
}
function countCharacter_indexOf(str, char) {
var start = 0;
var count = 0;
while ((start = str.indexOf(char, start) + 1) !== 0) {
count++;
}
return count;
}
performance of them by running 1,000,000 times on counting '/' in a string.
-- case1: running 1000000 times on ( 'this/is/a/path/with/extension', '/' )
countCharacter_reduce: 2389.880ms
countCharacter_regex: 496.251ms
countCharacter_split: 114.709ms
countCharacter_for: 152.012ms
countCharacter_indexOf: 90.330ms
-- case2: running 1000000 times on ( '////////////', '/' )
countCharacter_reduce: 1138.051ms
countCharacter_regex: 619.886ms
countCharacter_split: 121.945ms
countCharacter_for: 74.542ms
countCharacter_indexOf: 204.242ms
Conclusion ('>' means 'has better performance'):
for|split|indexOf > regex > reduce.
furthermore,
if the string contains more searching characters (like case2),
for>split>indexOf,
otherwise (like case1)
indexOf > split > for.
BTW: you can change the for indexOf method to fit multi-characters search (example is single character)
using reduce:
function countCharacter(str, char) {
return str.split('').reduce((a, x) => x === char ? ++a : a, 0);
}
I guess this involves regex which you wanted to avoid but it's pretty quick:
function countCharacter(str, char) {
return str.length - str.replace(new RegExp(char,"g"),"").length;
}
You can also try the str.split(char).length-1 approach suggested by Jaromanda.
Or, go all out with some fun recursion (pass 0 to startingFrom):
function countCharacter(str, char, startingFrom) {
var idx = str.indexOf(char, startingFrom);
return idx == -1 ? 0 : 1 + countCharacter(str, char, idx + 1);
}
You can get rid of the annoying extra argument at the cost of some efficiency:
function countCharacter(str, char) {
var idx = str.indexOf(char);
return idx == -1 ? 0 : 1 + countCharacter(str.substr(idx+1), char);
}
And here's a version optimized for speed (this is about 3 times faster on my browser than your original and much faster than the regex versions, according to jsperf):
function countCharacter(str, char) {
var start = 0;
var count = 0;
while((start = str.indexOf(char, start)+1) !== 0) {
count++;
}
return count;
}
Note that the indexOf approaches will generally be substantially faster than manually iterating through the string. See jsperf
Here you go. One line code
"hello".match(new RegExp('l','g')).length
replace 'l' with any char here, new RegExp('l','g').
that is
str.match(new RegExp(char,'g')).length
Have you thought of using the split() method? How about this -
function myFunction(str, char) {
return string.split(char).length - 1
}
Let me know what you think of this solution.
I have a recursion function which prints all the valid combinations of balanced parentheses as follows :
function addParen(upstock, downstock, sa)
{
if (upstock == 0 && downstock == 0)
{
Print(sa);
}
if (upstock > 0)
{
addParen(upstock - 1, downstock + 1, sa + "(");
}
if (downstock > 0)
{
addParen(upstock, downstock - 1, sa + ")");
}
}
It directly prints the result as strings like "((()))" or "()()()" for n=3 ( we assume 3 pairs, number of pairs is not important). However I want my recursive function to print one by one each parenthesis whenever the initial empty string concatenated with a "(" or ")".For instance for the first combination I want it to print like "(" then "(" then "(" then ")" then ")" and then ")".Then it can proceed with the same way for the second combination.Is it possible to do it ?
yours is not working with the balanced parenthesis because you need to make another checks. Bellow I am putting the result with the total number of combinations.
function addParen(n, upstock, downstock, sa){
var count = 0;
if(sa.length == 2 * n && upstock == downstock){
console.log(sa);
//number of valid combinations
return 1;
}
if(upstock >= downstock && upstock <= n){
count += addParen(n, upstock + 1, downstock, sa + "(");
}
if(downstock < upstock){
count += addParen(n, upstock, downstock + 1, sa + ")");
}
return count;
}
function numberOfBalancedParenthesis(n){
return addParen(n, 0, 0, "");
}
//try this on the console
numberOfBalancedParenthesis(2)
Hope this is what you're looking for!
function addParen( upstock, downstock ) {
if (upstock > 0 )
{
Print("(");
addParen(upstock - 1, downstock+1);
}
if (downstock > 0 )
{
Print(")");
addParen(upstock, downstock - 1);
}
}
I am working on a javascript code to find the nth occurrence of a character in a string. Using the indexOf() function we are able to get the first occurrence of the character. Now the challenge is to get the nth occurrence of the character. I was able to get the second third occurrence and so on using the code given below:
function myFunction() {
var str = "abcdefabcddesadfasddsfsd.";
var n = str.indexOf("d");
document.write("First occurence " +n );
var n1 = str.indexOf("d",parseInt(n+1));
document.write("Second occurence " +n1 );
var n2 = str.indexOf("d",parseInt(n1+1));
document.write("Third occurence " +n2 );
var n3 = str.indexOf("d",parseInt(n2+1));
document.write("Fourth occurence " +n3);
// and so on ...
}
The result is given below
First occurence 3
Second occurence 9
Third occurence 10
Fourth occurence 14
Fifth occurence 18
Sixth occurence 19
I would like to generalize the script so that I am able to find the nth occurrence of the character as the above code requires us to repeat the script n times. Let me know if there is a better method or alternative to do the same. It would be nice if we just give the occurrence (at run time) to get the index of that character.
The following are some of my questions:
How do we do it in JavaScript?
Does any framework provide any functionality to do the same implementation in an easier way or what are the alternate methods to implement the same in other frameworks /languages?
function nth_occurrence (string, char, nth) {
var first_index = string.indexOf(char);
var length_up_to_first_index = first_index + 1;
if (nth == 1) {
return first_index;
} else {
var string_after_first_occurrence = string.slice(length_up_to_first_index);
var next_occurrence = nth_occurrence(string_after_first_occurrence, char, nth - 1);
if (next_occurrence === -1) {
return -1;
} else {
return length_up_to_first_index + next_occurrence;
}
}
}
// Returns 16. The index of the third 'c' character.
nth_occurrence('aaaaacabkhjecdddchjke', 'c', 3);
// Returns -1. There is no third 'c' character.
nth_occurrence('aaaaacabkhjecdddhjke', 'c', 3);
You can do it easily by implementing a function using charAt(), like this:
function nth_ocurrence(str, needle, nth) {
for (i=0;i<str.length;i++) {
if (str.charAt(i) == needle) {
if (!--nth) {
return i;
}
}
}
return false;
}
alert( nth_ocurrence('aaaaacabkhjecdddchjke', 'c', 3) );//alerts 16
Thanks to CQQL for let me know what OP really wanted. I updated a bit my original function to achieve the new behaviour.
indexOf takes a second argument, the character index in the string to begin the search.
function nthChar(string, character, n){
var count= 0, i=0;
while(count<n && (i=string.indexOf(character,i)+1)){
count++;
}
if(count== n) return i-1;
return NaN;
}
var s= 'abcbbasdbgasdnnaabaasdert';
nthChar(s,'a',7);
So a nice way to do this is to extend the string class like so:
(function() {
String.prototype.nthOccurrenceIndex = function(charToMatch, occurrenceIndex) {
var char, index, matches, _i, _len;
matches = 0;
index = 0;
for (_i = 0, _len = this.length; _i < _len; _i++) {
char = this[_i];
if (char === charToMatch) {
matches += 1;
if (matches === occurrenceIndex) {
return index;
}
}
index += 1;
}
return -1;
};
}).call(this);
The much more concise CoffeeScript version:
String.prototype.nthOccurrenceIndex = (charToMatch, occurrenceIndex)->
matches = 0
index = 0
for char in #
if char is charToMatch
matches += 1
return index if matches is occurrenceIndex
index += 1
-1
So now you can do stuff like:
"abcabc".nthOccurrenceIndex('a', 1)
# -> 0
"abcabc".nthOccurrenceIndex('a', 2)
# -> 3
"abcabc".nthOccurrenceIndex('a', 3)
# -> -1
A maybe clearer function. Recursive and copy the mechanism of indexOf:
Doesn't cause an error if an incorrect number for nth (ie <= 0). It will return -1 like you can give a negative number (or greater than the length of the string) asfromIndex to indexOf.
Can take a fromIndex argument (the same than for indexOf: An integer representing the index at which to start the search; the default value is 0.)
function indexOfNth (string, char, nth, fromIndex=0) {
let indexChar = string.indexOf(char, fromIndex);
if (indexChar === -1){
return -1;
} else if (nth === 1) {
return indexChar;
} else {
return indexOfNth(string, char, nth-1, indexChar+1);
}
}
let test = 'string for research purpose';
console.log('first s:', indexOfNth(test, 's', 1));
console.log('second s:', indexOfNth(test, 's', 2));
console.log('15th s:', indexOfNth(test, 's', 15));
console.log('first z:', indexOfNth(test, 'z', 1));
console.log('-1th s:', indexOfNth(test, 's', -1));
console.log('first s starting from index=1:', indexOfNth(test, 's', 1, 1));
function nthIndexOf(search, n) {
var myArray = [];
for(var i = 0; i < myStr.length; i++) {
if(myStr.slice(i, i + search.length) === search) {
myArray.push(i);
}
}
return myArray[n - 1];
}