DSA: Trapping water problem solution issue - javascript

My brute solution works fine with a break condition handled by a map.
function trap_a(arr) {
let result = 0;
for (let i=1; i<arr.length-1; i++) {
const curr = arr[i];
let left = 0;
let right = 0;
let li = i-1;
let ri = i+1;
const hasNext = { left: true, right: true }; // <== here
while(hasNext.left || hasNext.right) { // <== here
left = Math.max(left, arr[li]);
right = Math.max(right, arr[ri]);
if (arr[li-1] != undefined) {
li--;
} else {
hasNext.left = false; // <== here
}
if (arr[ri+1] != undefined) {
ri++;
} else {
hasNext.right = false; // <== here
}
}
result += Math.min(left, right) - curr;
}
return Math.abs(result);
}
This compares the biggest on the left side of the current element and same on the right.
But when I try to simplify the code by NOT using a map, it gives a different answer (wrong):
function trap_b(arr) {
let result = 0;
for (let i=1; i<arr.length-1; i++) {
const curr = arr[i];
let left = 0;
let right = 0;
let li = i-1;
let ri = i+1;
while(arr[li-1] !== undefined || arr[ri+1] !== undefined) { // <== here
left = Math.max(left, arr[li]);
right = Math.max(right, arr[ri]);
if (arr[li-1] != undefined) {
li--;
}
if (arr[ri+1] != undefined) {
ri++;
}
}
result += Math.min(left, right) - curr;
}
return Math.abs(result);
}
Test case:
console.log(trap_a([4,1,0,3,2,5]) === 10); // passes
console.log(trap_b([4,1,0,3,2,5]) === 10); // fails
I am not sure what I am doing wrong in the second solution, it is literally doing the same thing. Am I missing something here?

In trap_b, when you decrement li (respectively, increment ri), you check a different index in the loop test instead of the same one, so the loop exits early.

It seems that trap_b is unable to iterate through the loop entirely. More specifically the right most element of the array is unreachable.
So in the first iteration, "right" is never updated to 5. This in turn results in
Math.max(left, right) - curr = Math.max(4, 3) - 1 = 2.
It should be:
Math.max(left, right) - curr = Math.max(4, 5) - 1 = 3.
Here is a small change that I made to the algorithm that ensures that left and right variables are updated correctly.
function trap_b(arr) {
let result = 0;
for (let i=1; i<arr.length-1; i++) {
const curr = arr[i];
let left = 0;
let right = 0;
let li = i-1;
let ri = i+1;
while(arr[li-1] !== undefined || arr[ri+1] !== undefined) { // <== here
left = Math.max(left, arr[li]);
right = Math.max(right, arr[ri]);
if (arr[li-1] != undefined) {
li--;
}
if (arr[ri+1] != undefined) {
ri++;
}
}
//Here is the change
left = Math.max(left, arr[li]);
right = Math.max(right, arr[ri]);
//end of change
result += Math.min(left, right) - curr;
}
return Math.abs(result);
}

Related

Develop a JavaScript to Find the position of 3rd occurrence of the given string “efg” I/P:abcefgacefgabcceftyefghjklop O/p:20

I developed a logic for the above problem but i couldnt get the desired result.
function thirdOccurence(string){
var count = 0;
var i=0;
var result = 0;
while(i<string.length){
if(string[i]=='e' && string[i+1]=='f' && string[i+2]=='g')
{
count = count+1;
i+3;
if(count==3)
{
result = i+1;
break;
}
}
else
i++;
}
}*
here the string is the input - abcefgacefgabcceftyefghjklop
-output should be 20 since we need to find position. I have a working code but with a different logic. But i need to know why this is not working.
The problem was with the line where you increment the i variable by 3: i += 3. This meant you jumped 3 indexes forward, before the final condition if(count==3). But if you increment i by 3 after this final condition - everything works as expected :)
function thirdOccurence(string) {
var count = 0;
var i = 0;
var result = 0;
while (i < string.length) {
if (string[i] === "e" && string[i + 1] === "f" && string[i + 2] === "g") {
count += 1;
if (count === 3) {
result = i + 1;
break;
}
i += 3;
} else {
i++;
}
}
return result
}
console.log('result should be 20:', thirdOccurence('abcefgacefgabcceftyefghjklop'))

Compression String does not work for all strings

The idea is to complete the function and produce a compressed form of the string given. An example would be if the given string was aabbcc then you would get a2b2c2
The issue with the code I created is for some reason it does not work with anything right away that is consecutive or consecutive letters at the end. wwww turns into w4 but aa does not turn into a2 and wuenuneubgnjfniwfibwiebfaaa will not turn into wuenuneubgnjfniwfibwiebfa3
function compressedString(message) {
let out = '';
let count = 1;
for (let i = 0; i < message.length; i++) {
let current = message[i];
let next = message[i + 1];
if (current == next) {
count++;
} else {
out += current + String(count);
count = 1;
}
}
}
I test your algorithm, using given example of your question wuenuneubgnjfniwfibwiebfaaa, the output was w1u1e1n1u1n1e1u1b1g1n1j1f1n1i1w1f1i1b1w1i1e1b1f1a3, what sounds strange for the string compression requirement. When I add a nested condition inner first else in for loop, I acquired the correct result, please view the code bellow and let what do you think about:
function compressedString(message) {
let out = '';
let count = 1;
for (let i = 0; i < message.length; i++) {
let current = message[i];
let next = message[i + 1];
if (current == next) {
count++;
} else {
if(count == 1){
out += current;
}else{
out += current + String(count);
}
count = 1;
}
}
return out;
}
Your issue is the way you are handling it in the end. What happens to the variable next when the loop is on the last iteration? You need to add an extra check on your if.
Try this:
function compressedString(message) {
let out = '';
let count = 1;
for (var i = 0; i < message.length; i++){
let current = message[i];
let next = message[i + 1];
if ( i < message.length-1 && message[i] == next) {
count += 1;
} else {
out += current + String(count);
count = 1;
}
}
return out;
}

How does this Sudoku backtracking recursive algorithm trigger backtracking?

let row = this.findEmpty(puzzleString)[0];
let col = this.findEmpty(puzzleString)[1];
let i = this.findEmpty(puzzleString)[2];
if(!this.findEmpty(puzzleString)) return puzzleString
for(let num = 1; num < 10; num++){
if(this.checkValue(puzzleString, row, col, num)){
puzzleString[i] = num;
this.solve(puzzleString)
}
}
findEmpty(puzzleString) iterates over the puzzle string and returns the row (A-I), column (1-9), and index of a blank grid.
checkValue() contains 3 helper functions returning a boolean ensuring there are no conflicts across row, column, or region.
The loop iterates from 1-9 and the first value from 1-9 that passes checkValue() is assigned to the current blank grid and then triggers recursion by calling the parent function solve().
What I don't understand is the next statement and how that triggers backtracking.
if(this.findEmpty(puzzleString)){
puzzleString[i] = '.';
}
If the current blank grid being checked has no solution then I think the grid remains a blank ('.'). If this is correct, why is this statement necessary? What about this statement is triggering backtracking?
My initial inclination is that this statement is a psuedo-else statement that runs only if the loop fails to find a solution. It has to be placed outside the loop in order to allow the full iteration of 1 through 9. But then how does the code know to run solve() afterwards if solve() is only called if checkValue() suceeds?
Here's the full code:
solve(puzzleString) {
let row = this.findEmpty(puzzleString)[0];
let col = this.findEmpty(puzzleString)[1];
let i = this.findEmpty(puzzleString)[2];
if(!this.findEmpty(puzzleString)) return puzzleString
for(let num = 1; num < 10; num++){
if(this.checkValue(puzzleString, row, col, num)){
puzzleString[i] = num;
this.solve(puzzleString)
}
}
if(this.findEmpty(puzzleString)){
puzzleString[i] = '.';
}
if(puzzleString.includes('.')) return { error: 'Puzzle cannot be solved' }
return {
solution: puzzleString.join('')
}
}
findEmpty(puzzleString){
for(let i = 0; i < puzzleString.length; i++){
if(puzzleString[i] == '.'){
let row = String.fromCharCode('A'.charCodeAt(0) + Math.floor(i / 9));
let col = (i % 9) + 1;
return [row, col, i];
}
}
return false;
}
checkValue(puzzleString, row, column, value){
if(this.checkRowPlacement(puzzleString, row, column, value)&&
this.checkColPlacement(puzzleString, row, column, value)&&
this.checkRegionPlacement(puzzleString, row, column, value)){
return true;
}
return false;
}
checkRowPlacement(puzzleString, row, column, value) {
let coordinates = [];
let rowLetter;
let colNum;
let temp = [];
if(row){row = row.toUpperCase();}
for(let i = 0; i < puzzleString.length; i++){
rowLetter = String.fromCharCode('A'.charCodeAt(0) + Math.floor(i / 9));
colNum = (i % 9) + 1;
coordinates.push(rowLetter + colNum);
}
for(let i = 0; i < coordinates.length; i++){
if(coordinates[i][0] == row){
temp.push(puzzleString[i]);
}
}
temp = temp.join('');
return !temp.includes(value) ? true : false
}
checkColPlacement(puzzleString, row, column, value) {
let coordinates = [];
let rowLetter;
let colNum;
let temp = [];
if(row){row = row.toUpperCase();}
for(let i = 0; i < puzzleString.length; i++){
rowLetter = String.fromCharCode('A'.charCodeAt(0) + Math.floor(i / 9));
colNum = (i % 9) + 1;
coordinates.push(rowLetter + colNum);
}
for(let i = 0; i < coordinates.length; i++){
if(coordinates[i][1] == column){
temp.push(puzzleString[i]);
}
}
temp = temp.join('');
return !temp.includes(value) ? true : false
}
checkRegionPlacement(puzzleString, row, column, value) {
let coordinates = [];
let rowLetter;
let colNum;
let regions = [];
if(row) row = row.toUpperCase();
for(let i = 0; i < puzzleString.length; i++){
rowLetter = String.fromCharCode('A'.charCodeAt(0) + Math.floor(i / 9));
colNum = (i % 9) + 1;
coordinates.push(rowLetter + colNum);
}
for(let i = 0; i < coordinates.length; i+=27){
for(let k = 0; k < 9; k+=3){
regions.push(
coordinates.slice(i+k,i+k+3) + ',' +
coordinates.slice(i+k+9, i+k+12) + ',' +
coordinates.slice(i+k+18, i+k+21)
)
}
}
let region = regions.filter(x => x.includes(row + column))[0].split(',').map(x => puzzleString[coordinates.indexOf(x)]).join('');
return region.includes(value) ? false : true;
}
The backtracking happens when findEmtpy returns false. But as your code is not optimal, still many other options are tried while backtracking: none of the for loops that are pending in the recursion tree are interrupted, yet it is wasted effort to have them continue and calling checkValue as each of those calls will now return false. So, eventually all those for loops will end, and the recursion will backtrack, only to finish yet another loop and backtrack again, ...etc.
Here is an update of your main function to avoid some of that overhead that leads to no gain:
solve(puzzleString) {
// Only call findEmpty once!
let emptyCell = this.findEmpty(puzzleString);
if (!emptyCell) return { solution: puzzleString.join('') }; // return the success object
let [row, col, i] = emptyCell; // use destructuring assignment
for (let num = 1; num < 10; num++) {
if (this.checkValue(puzzleString, row, col, num)) {
puzzleString[i] = num;
let result = this.solve(puzzleString); // capture the return value
if (result.solution) return result; // success: fast backtracking!
}
}
puzzleString[i] = "."; // could not solve this spot
// backtrack to possibly take a different route in previous decisions
return { error: 'Puzzle cannot be solved' };
}

How do I find if an array has every element occuring twice except one OR an array with only one element occuring twice?

I am trying to solve the following kata : https://www.codewars.com/kata/583ef2456e39941f810001c5/javascript
Here's what I have attempted so far :
function countFrequency(arr){
let countMap = {
};
for(let i = 0 ; i < arr.length; i++){
if(countMap[arr[i]] == null){
countMap[arr[i]] = 1;
}
else{
countMap[arr[i]] += 1;
}
}
return countMap;
}
function duplicateOrUnique(arr){
let type1 = true;
countMap = countFrequency(arr);
let count = 0;
for(let key in countMap){
if(countMap[key] >= 2){
count+=1;
}
if(count >= 2){
type1 = false;
break;
}
}
if(type1){
arrSum = arr => arr.reduce((a,b) => a + b, 0);
k = arr.length -1 ;
return Math.abs(arrSum(arr) - (k*(k + 1)/2));
}
else{
arrXor = arr => arr.reduce((a,b) => a ^ b, 0);
return arrXor(arr)
}
}
I have used n*(n+1)/2 formula to find out the repeated element in case 1 while I have used XOR operation to find the only unique element. This process is supposed to be faster than the current solution. How do I optimize this?

longest substring of non repeating characters javascript

The problems asks "given a string, find the longest non-repeating sub-string without repeating characters". I am a little stumped why returning my code is not working for the string "dvdf" for example. Here is my code :
function lengthOfLongestSubstring(check) {
var letters = check.split("");
var max = 0;
var result = [];
for (var i = 0; i < letters.length; i++) {
var start = i
if (result.indexOf(letters[i]) === -1) {
result.push(letters[i])
} else {
i = i - 1
result = []
}
if (max === 0 || max < result.length) {
max = result.length
}
}
return max
}
This implementation gives the correct result for "dvdf".
It adds characters to current_string while there is no duplicate. When you find a duplicate cut current_string to the point of the duplicate. max is the max length current_string had at any time. This logic seems correct to me so I think it's correct.
function lengthOfLongestSubstring(string) {
var max = 0, current_string = "", i, char, pos;
for (i = 0; i < string.length; i += 1) {
char = string.charAt(i);
pos = current_string.indexOf(char);
if (pos !== -1) {
// cut "dv" to "v" when you see another "d"
current_string = current_string.substr(pos + 1);
}
current_string += char;
max = Math.max(max, current_string.length);
}
return max;
}
lengthOfLongestSubstring("dvdf"); // 3
The value of current_string in each round is "", "d", "dv", "vd", "vdf".
By replacing the result array with a map storing the last index for each encountered character, you can modify the loop body to jump back to one after the last index of an identical character and continue your search from there instead of just restarting from the current position via currently i = i - 1 which fails in cases such as 'dvdf':
Below is your code with changes to accommodate a map in place of an array:
function lengthOfLongestSubstring(check) {
var letters = check.split("");
var max = 0;
var result = new Map();
var start = 0;
for (var i = 0; i < letters.length; i++) {
if (!result.has(letters[i])) {
result.set(letters[i], i);
} else {
i = result.get(letters[i]);
result.clear();
}
if (max < result.size) {
max = result.size;
}
}
return max;
}
// Example:
console.log(lengthOfLongestSubstring("dvdf")); // 3
Here's a solution using Sliding window and HashMap.
var lengthOfLongestSubstring = function(str) {
if (!!!str.length || typeof str !== 'string') return 0;
if (str.length == 1) return 1;
let hashTable = {};
let longestSubstringLength = 0;
let start = 0;
for (let i = 0; i < str.length; i++) {
if (hashTable[str[i]] !== undefined && hashTable[str[i]] >= start) {
start = hashTable[str[i]] + 1;
}
hashTable[str[i]] = i;
longestSubstringLength = Math.max(longestSubstringLength, (i - start + 1))
}
return longestSubstringLength;
}
I figured out an easier solution:
function longestSubstring(str) {
let left = 0;
let max = 0;
let result = new Set();
for (let r = 0; r < str.length; r++) {
//The code will check for an existing item on the set
// If found, all the previously saved items will be deleted
// the set will return to being empty
while (result.has(str[r])) {
result.delete(str[left]);
left += 1;
}
result.add(str[r]);
max = Math.max(max, r - left + 1);
}
console.log(result);
return max;
}
console.log(longestSubstring('abcabccbc')); //3
Today (January 7th, 2021) this was the Leetcode question of the day. I initially used a solution very similar to the selected answer. Performance was okay but after reviewing the answer solution documentation I rewrote my answer using the sliding window technique (examples were only in Java and Python) since I was curious about how much of a performance improvement this would result in. It is slightly more performant (144ms versus 160ms) and has a lower memory footprint (42mb versus 44.9mb):
function lengthOfLongestSubstring(s: string): number {
let stringLength = s.length;
let maxLength = 0;
const charMap = new Map();
let pos = 0;
for (let i = 0; i < stringLength; i++) {
if (charMap.has(s[i])) {
pos = Math.max(charMap.get(s[i]), pos);
}
maxLength = Math.max(maxLength, i - pos + 1);
charMap.set(s[i], i + 1);
}
return maxLength;
}
console.log(lengthOfLongestSubstring("dvdf"));
Try this:
function lengthOfLongestSubstring (str) {
const map = new Map();
let max = 0;
let left = 0;
for (let right = 0; right < str.length; right++) {
const char = str[right];
if (map.get(char) >= left) left = map.get(char) + 1;
else max = Math.max(max, right - left + 1);
map.set(char, right);
}
return max;
}
You can try this:
function lengthOfLongestSubstring(str) {
let longest = "";
for (let i = 0; i < str.length; i++) {
if (longest.includes(str[i])) {
return longest.length
} else {
longest += str[i];
}
}
return longest.length;
}
console.log(lengthOfLongestSubstring("abcabcbb"));
console.log(lengthOfLongestSubstring("bbbbb"));
console.log(lengthOfLongestSubstring("abcdef"));
console.log(lengthOfLongestSubstring(""));
reset i to i -1 is incorrect. you need another loop inside the for loop. you try something like this (i didn't check the index carefully).
function lengthOfLongestSubstring(check){
var letters = check.split("");
var max = 0;
for (var i = 0; i < letters.length; i++) {
var result = [];
var j = i;
for(;j < letters.length; j++) {
if (result.indexOf(letters[j]) === -1) {
result.push(letters[j]);
} else {
break;
}
}
if(j - i > max) {
max = j - i;
}
}
return max;
}
You can try sliding window pattern to solve this problem.
function lengthOfLongestSubstring(str) {
let longest = 0;
let longestStr = "";
let seen = {};
let start = 0;
let next = 0;
while (next < str.length) {
// Take current character from string
let char = str[next];
// If current character is already present in map
if (seen[char]) {
// Check if start index is greater than current character's last index
start = Math.max(start, seen[char]);
}
// If new substring is longer than older
if (longest < next - start + 1) {
longest = next - start + 1;
// Take slice of longer substring
longestStr = str.slice(start, next + 1);
}
// Update current characters index
seen[char] = next + 1;
// Move to next character
next++;
}
console.log(str, "->", longestStr, "->", longest);
return longest;
}
lengthOfLongestSubstring("dvdfvev");
lengthOfLongestSubstring("hello");
lengthOfLongestSubstring("1212312344");
Find Longest Unique Substring using Map Method
var str = "aaabcbdeaf";
var start = 0;
var map = new Map();
var maxLength = 0;
var longStr = '';
for(next =0; next< str.length ; next++){
if(map.has(str[next])){
map.set(str[next],map.get(str[next])+1);
start = Math.max(start,map.get(str[next]));
}
if(maxLength < next-start+1){
maxLength = next-start+1;
longStr = str.slice(start,next+1);
}
map.set(str[next],next);
}
console.log(longStr);
You can try something like that:
function maxSubstring(s) {
const array = []
const lengthS = s.length
const pusher = (value) => {
if (value !== '') {
if (array.length > 0) {
if (array.indexOf(value) === -1) {
array.push(value)
}
} else {
array.push(value)
}
}
}
pusher(s)
for (const [index, value] of s.split('').entries()) {
let length = lengthS
let string = s
const indexO = s.indexOf(value)
pusher(value)
while (length > indexO) {
pusher(string.slice(index-1, length + 1))
length = --length
}
string = s.slice(index, lengthS)
}
array.sort()
return array.pop()
}
console.log(maxSubstring('banana'))
console.log(maxSubstring('fgjashore'))
console.log(maxSubstring('xyzabcd'))
Find Longest unique substring without using MAP(). Just simple slice().
The same can be used to return longest unique string.
Just replace "return max => return str"
const string = "dvdf";
var lengthOfLongestSubstring = function() {
if(string.length == 1) return 1;
if(string.length == 0) return 0;
let max = 0,i = 0, str = "";
while(i < string.length){
const index = str.indexOf(string.charAt(i));
if(index > -1) {
// s = "fiterm".slice(1,4) => ite
str = str.slice(index + 1, string.length);
}
str += string.charAt(i);
max = Math.max(str.length, max);
i++;
}
return max;
};
Logest unqiue substring:
function lengthOfLongestSubstring(s) {
if(s.length < 2) {
return s.length;
}
let longestLength = 1;
let currentStr = '';
for(let i=0 ; i < s.length ; i++){
if(currentStr.includes(s.charAt(i))){
let firstSeen = currentStr.indexOf(s.charAt(i));
currentStr = currentStr.substring(firstSeen+1,currentStr.length);
}
currentStr += s.charAt(i);
longestLength = Math.max(currentStr.length,longestLength);
}
return longestLength;
};
One liner with reduce method.
const subStrOfUniqueChar = str => [...str].reduce((p,c) => ( p.includes(c) ? (p += c, p.substr(p.indexOf(c)+1)) : p += c),'');
console.log(subStrOfUniqueChar('dvdf').length);
function lengthOfLongestSubstring(s: string): number {
const arr = s.split("");
let longest = 0;
const set: Set<string> = new Set();
for (let i = 0; i < arr.length; i++) {
set.add(arr[i]);
let tryIndex = i + 1;
while (arr[tryIndex] && !set.has(arr[tryIndex])) {
set.add(arr[tryIndex]);
tryIndex++;
}
if (set.size > longest) {
longest = set.size;
}
set.clear();
}
return longest;
}
I wanted to toss my hat in this ring because I feel like I've found a pretty creative solution to this. No if/else blocks are needed as the substring.indexOf() will attempt to find the matching string character in the array and delete the indexes of the array up to, and including, the match (+1). If an indexOf() call finds no match it will return a -1, which added to +1 becomes a .splice(0,0) which will remove nothing. The final Math check factors in the last character addition in the loop to determine which outcome is higher.
const findSubstring = string => {
let substring = [];
let maxCount = 0;
for (let i = 0; i < string.length; i++) {
maxCount = Math.max(substring.length, maxCount);
substring.splice(0, substring.indexOf(string[i]) + 1);
substring.push(string[i]);
}
maxCount = Math.max(substring.length, maxCount);
return maxCount;
}
uses sliding window concept
function lengthOfLongestSubstring(s) {
var letters = s.split("");
var subStr = "";
var result = [];
var len = 0;
let maxLen = 0;
for (var i = 0; i < letters.length; i++) {
const position = result.indexOf(letters[i]);
if (position === -1) {
result.push(letters[i]);
len += 1;
} else if (letters[i]) {
result = result.splice(position + 1);
len = result.length + 1;
result.push(letters[i]);
}
maxLen = len > maxLen ? len : maxLen;
}
return maxLen;
}
console.log(lengthOfLongestSubstring(" "));
Sliding Window Technique O(n)
you can use hash or Map in
loop through string char
Maintain dictionary of unique char
if char exist in memory take clear hash update the count in longest variable and clear count
start from first repeated char + 1 again.
var lengthOfLongestSubstring = function(s) {
if(s.length<2) return s.length;
let longest = 0;
let count=0;
let hash={}
for (let i = 0; i < s.length; i++) {
//If char exist in hash
if(hash[s[i]]!=undefined){
i=hash[s[i]];
hash={}
longest = Math.max(longest, count);
count = 0;
}else{
hash[s[i]]=i
count = count+1;
}
}
return Math.max(longest, count);
};
console.log(lengthOfLongestSubstring("abcabcbb"))
console.log(lengthOfLongestSubstring("au"))

Categories