Related
I am looking at LeetCode problem 2246. Longest Path With Different Adjacent Characters:
You are given a tree (i.e. a connected, undirected graph that has no cycles) rooted at node 0 consisting of n nodes numbered from 0 to n - 1. The tree is represented by a 0-indexed array parent of size n, where parent[i] is the parent of node i. Since node 0 is the root, parent[0] == -1.
You are also given a string s of length n, where s[i] is the character assigned to node i.
Return the length of the longest path in the tree such that no pair of adjacent nodes on the path have the same character assigned to them.
This is my code:
function longestPath(parent: number[], s: string): number {
const childrenMap: Record<number, number[]> = {}
for (let node = 0; node < parent.length; node++) {
const parentNode = parent[node]
childrenMap[parentNode] = childrenMap[parentNode] ?? []
childrenMap[parentNode].push(node)
}
let max = 0
const dfs = (node = 0, parentNode = -1): number => {
if (s[node] === s[parentNode]) {
max = Math.max(max, dfs(node, -1))
return 0
}
const [a = 0, b = 0] = (childrenMap[node] ?? []).map((child) => dfs(child, node))
.sort((la, lb) => lb - la)
max = Math.max(max, 1 + a + b)
return a + 1
}
dfs()
return max
}
console.log(
longestPath(
[-1, 56, 10, 79, 52, 0, 37, 39, 127, 125, 116, 52, 95, 131, 105, 55, 55, 52, 87, 35, 43, 130, 87, 103, 8, 73, 8, 116, 4, 43, 60, 104, 116, 118, 78, 9, 133, 139, 7, 127, 96, 28, 52, 79, 78, 36, 102, 134, 100, 104, 47, 127, 129, 77, 121, 133, 10, 58, 104, 55, 69, 73, 107, 9, 139, 79, 52, 72, 130, 78, 112, 43, 14, 4, 120, 9, 139, 118, 52, 52, 73, 82, 79, 58, 121, 80, 139, 10, 25, 74, 10, 123, 134, 112, 40, 80, 108, 128, 5, 52, 43, 31, 10, 42, 79, 139, 86, 58, 3, 118, 117, 21, 4, 79, 45, 26, 5, 122, 102, 13, 88, 139, 108, 118, 116, 10, 58, 32, 80, 125, 121, 105, 116, 104, 82, 131, 39, 10, 126, 125],
'vodpyvpjmogqvwnibqasbulkfbfugvtlpdtsmydrbrekavkhifoypepbcnzpmasbnlrfqgdhnmvhldsrogjsntummchcftzrnycichziopmphfqwqdsihoywdpqqkyvzrhbqorwrkmns',
),
)
It outputs 13, but the expected output is 17.
When I replace
max = Math.max(max, dfs(node, -1))
with
max = Math.max(dfs(node, -1), max)
the answer will be right (17). I can't tell the difference... What's wrong?
Your dfs changes max in two places:
max = Math.max(
max,
dfs(node, -1),
)
...
max = Math.max(max, 1 + a + b)
return a + 1
So the order in which you call dfs as an argument to Math.max can affect things:
If you call dfs first, it will change max and the second argument passed to Math.max will be the changed value, so it will compare with the correct value that was changed by the recursive dfs calls.
If you call dfs second, it will copy max, call dfs then compare it with the old max, with the value it had before the recursive call.
Solution:
The best thing you can do is not mutate state external to dfs: make dfs return what you need and use that returned value, don't rely on an external variable. You seem to already be returning something from dfs, so it shouldn't be too hard to get rid of mutating max.
That can be messy in some cases (but not yours), in those cases at least don't put the recursive call as a parameter. Do it like this:
const dfsResult = dfs(node, -1)
max = Math.max(max, dfsResult)
I need help with the distribution of routes by day of the month.
The situation is as follows. There are, for example, 3 zones, each zone has routes (not always the same number).
The task is to distribute routes by weekdays in a month, but there is a condition: routes from two NOT zones can be assigned for one day, zones must not overlap.
Example:
Routes of 1 zone: *1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 , 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43; *
Routes 2 zones: 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 , 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79;
Routes 3 zones: 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 , 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120;
It turns out 120 routes (for example) per month. Let's say there are 20 days in a month, respectively, we divide 120 by 20, we get 6 routes per day.
As a result, we should get the distribution of routes by working days of the week in the following form:
1-6,7-12,13-18,19-24,25-30,31-36,37-43,44-49,50-55,56-61,62-67,68-73,74-79,80-85,86-91,92-97,98-103,104-109,110-115,116-120
As is clear from the number of "number-number" blocks (from which route by which), this is distributed over 20 days.
You can also see that the routes do not intersect zones, i.e. in 1 day there is no such thing:
"43-49", because route 43 is from the 1st zone, and the rest from the 2nd.
As a result, I began to write an algorithm to distribute everything and I could not think of anything better (so far) how to create a two-dimensional array, i.e. an array in which there are arrays with zones, these arrays have route numbers.
Then I wrote a function that distributes these routes without mixing them between zones by recursion and creating a THREE-DIMENSIONAL array.
Then I print the first and last element of each array and get, for example, a string:
'1-6,7-12,13-18,19-24,25-30,31-36,37-43,44-49,50-55,56-61,62-67,68-73,74-79,80-85,86-91,92-97,98-103,104-109,110-115,116-120';
P.S. I had to fix it a little, because it was the case that depending on the number of working days in a week, there could be 1 routes per day, which is bad, so I added a feature that iterates over an array of arrays and if the length of the nested is less than or equal to three (the number of routes is less than or equal to 3m), then we put it in the previous day (as it were)
In theory, everything works, but there is a huge BUT. It does not always work adequately due to the different number of working days in a month. For example: In a month there are 22 working days, and the algorithm divides everything in total into 19 blocks, which corresponds to 19 days, or if 17 working days, then divides into 21 blocks, which is equal to 21 days (BUT THIS IS ONLY AN EXAMPLE, real the numbers can be seen when testing my code).
Can you please tell me how I can correct the code so that the number of allocated blocks is strictly equal to the number of working days (does not depend on the number of working days)?
// Array a - routs
const a = [
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43],
[44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120]
];
const days = 20;
const countRoutsInDay = Math.floor(a.flat().length / days);
const newArr = [];
const resultArr = [];
let str = '';
// Divide into arrays by day
const splitterRouts = (count) => {
let arr = [];
let currentElemOfMainArray = count;
for (let i = 0; i < Math.ceil(a[count].length / countRoutsInDay); i++) {
arr[i] = a[count].slice((i * countRoutsInDay), (i * countRoutsInDay) + countRoutsInDay);
if (i === Math.ceil(a[count].length / countRoutsInDay) - 1) {
currentElemOfMainArray += 1;
newArr.push(arr);
if (currentElemOfMainArray < a.length) {
splitterRouts(currentElemOfMainArray);
}
}
}
}
splitterRouts(0);
// If there are less than or equal to 3 routes, then connect on the previous day
newArr.forEach((el) => {
el.forEach((el2) => {
if (el2.length <= 3) {
const summRoute = el[el.length - 2].concat(el[el.length - 1]);
el.splice(el.indexOf(el[el.length - 2]), el.indexOf(el[el.length - 1]), summRoute);
}
})
});
newArr.forEach((el) => {
el.forEach((el2) => {
str += `${el2[0]}-${el2[el2.length - 1]},`
resultArr.push(str.split(','));
})
})
console.log(str);
console.log(resultArr.length - 1); // this result must be equal to the number of days given above
The answer depends a bit on one decision: what should happen to zone 3 (41 routs) if there are 22 days? Should it be split into 6 days of 5 routs and one day of 11 routs? Or should the routs be evenly distributed with six days of 7 routs and one day of 6 routs? If it's the former, then you'll need to change the bit of code regarding "less than or equal to 3" to append any excess routs to the last day for that zone. Assuming you want the days more distributed more evenly, this solution accomplishes the second case:
First, days are distributed across all the zones (with the biggest zones getting any remainder). Next, within each zone, routs are distributed across days (with the later days getting more routs):
// Array a - routs
const a = [
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43],
[44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120]
];
const days =20;
//evenly distributing the days among the zones, leaving any remaining days on the longest zone(s)
const countRoutsInDay2 = a.map((aInnerArr,i)=>[aInnerArr.length,i]).sort()/*.map(a=>a[1])*/.reverse();
const countRoutsInDay3 = [];
for (let i = 0; i < a.length; i++){
countRoutsInDay3[i] = Math.floor(days/a.length);
if (i >= (a.length - (days % (Math.floor(days/a.length))) )) {countRoutsInDay3[i]++};
}
countRoutsInDay3.reverse();
let countDaysForZone = [];
for (let i = 0; i < a.length; i++){
countDaysForZone[countRoutsInDay2[i][1]] = countRoutsInDay3[i];
}
const newArr = [];
const resultArr = [];
let str = '';
let countRoutsInDay = 5;
// similarly, dividing each zone into days as evenly as possible, with the longer days later
const splitterRouts = (count) => {
let arr = [];
let currentElemOfMainArray = count;
let routsOnThisDay = [];
let daysForThisArray = countDaysForZone[count];
for (let i = 0; i < daysForThisArray; i++){
routsOnThisDay[i] = Math.floor(a[count].length/daysForThisArray);
if (i >= (daysForThisArray - (a[count].length % daysForThisArray) )) {routsOnThisDay[i]++};
}
for (let i = 0; i < countDaysForZone[count]; i++) {
let routsSoFar = 0;
if (i==1){routsSoFar = routsOnThisDay[0];}
if (i>1){
routsSoFar = routsOnThisDay.slice(0,i).reduce((a,b)=>a+b);
}
arr[i] = a[count].slice(routsSoFar, routsSoFar + routsOnThisDay[i]);
if (i === countDaysForZone[count] - 1) {
currentElemOfMainArray += 1;
newArr.push(arr);
if (currentElemOfMainArray < a.length) {
splitterRouts(currentElemOfMainArray);
}
}
}
}
splitterRouts(0);
// If there are less than or equal to 3 routes, then connect on the previous day
newArr.forEach((el) => {
el.forEach((el2) => {
if (el2.length <= 3) {
const summRoute = el[el.length - 2].concat(el[el.length - 1]);
el.splice(el.indexOf(el[el.length - 2]), el.indexOf(el[el.length - 1]), summRoute);
}
})
});
newArr.forEach((el) => {
el.forEach((el2) => {
str += `${el2[0]}-${el2[el2.length - 1]},`
resultArr.push(str.split(','));
})
})
console.log(str);
console.log(resultArr.length - 1);
I was trying to solve this problem with backtracking instead of DP...and ran into a problem that doesn't make sense to me. here is my code
function solve(A) {
let lp = 0;
const path = [];
const traverse = function (arr, i = 0) {
if (i >= arr.length) return;
for (j = i; j < arr.length; j++) {
if (path.length === 0 || arr[j] > path[path.length - 1]) {
path.push(arr[j]);
// here is the problem I don't understand, I am calling the recursive function with( j + 1)
// but somehow that reads it as j = j + 1; why is this happening?
// I would understand if it was an Array where the j would pointing to memory but it shouldn't // happen to a regular number, right?
traverse(arr, j + 1);
// this causes the j variable in the for loop to increment by 1 so I can't get back track properly.
if (path.length > lp) lp = path.length;
path.pop();
}
}
}
traverse(A);
return lp;
}
const A = [69, 54, 19, 51, 16, 54, 64, 89, 72, 40, 31, 43, 1, 11, 82, 65, 75, 67, 25, 98, 31, 77, 55, 88, 85, 76, 35, 101, 44, 74, 29, 94, 72, 39, 20, 24, 23, 66, 16, 95, 5, 17, 54, 89, 93, 10, 7, 88, 68, 10, 11, 22, 25, 50, 18, 59, 79, 87, 7, 49, 26, 96, 27, 19, 67, 35, 50, 10, 6, 48, 38, 28, 66, 94, 60, 27, 76, 4, 43, 66, 14, 8, 78, 72, 21, 56, 34, 90, 89]
const B = [1,3,4,2,5,3]
console.log(solve(A))
console.log(solve(B))
an explanation would be appreciated, thanks in advance!!!
I want to iterate through an Array to find the index of the highest numbers on it, then write those index numbers on another new Array.
I came up with this code:
let scores = [60, 50, 60, 58, 54, 54, 58, 50, 52, 54, 48, 69, 34, 55, 51, 52, 44, 51, 69, 64, 66, 55, 52, 61, 46, 31, 57, 52, 44, 18, 41, 53, 55, 61, 51, 44];
let highestScore = Math.max(...scores);
let topScores = [];
for (score of scores) {
if (score == highestScore) {
topScores.push(scores.indexOf(score));
}
}
console.log(topScores);
Then the result that the console shows is:
topScores = [11, 11]
...when I expected:
topScores = [11, 18]
as those are the positions where the highest numbers (both 69) are on the scores array.
Can someone explain to me what's happening? I searched but I can't come up with the issue. Thank you very much.
As mentioned by Fritz Array.indexOf(x) always returns the first position of x in the array. The first 69 is at index 11.
You can use Array.forEach() instead of for...of:
let scores = [60, 50, 60, 58, 54, 54, 58, 50, 52, 54, 48, 69, 34, 55, 51, 52, 44, 51, 69, 64, 66, 55, 52, 61, 46, 31, 57, 52, 44, 18, 41, 53, 55, 61, 51, 44];
let highestScore = Math.max(...scores);
let topScores = [];
scores.forEach((score, index) => {
if (score == highestScore) {
topScores.push(index);
}
})
console.log(topScores);
This is beacause indexOf always return the first index at which element exist in array. You can use reduce()
let scores = [60, 50, 60, 58, 54, 54, 58, 50, 52, 54, 48, 69, 34, 55, 51, 52, 44, 51, 69, 64, 66, 55, 52, 61, 46, 31, 57, 52, 44, 18, 41, 53, 55, 61, 51, 44];
let highestScore = Math.max(...scores);
let res = scores.reduce((ac,a,i) => (a === highestScore && ac.push(i),ac),[])
console.log(res)
As others have already mentioned, both indexOf and findIndex always return the index of the first match. But there is no need for using either because you can access the current index in for..of like this:
for (const [index, score] of scores.entries())
which allows you to simply do
topScores.push(index);
let scores = [60, 50, 60, 58, 54, 54, 58, 50, 52, 54, 48, 69, 34, 55, 51, 52, 44, 51, 69, 64, 66, 55, 52, 61, 46, 31, 57, 52, 44, 18, 41, 53, 55, 61, 51, 44];
let highestScore = Math.max(...scores);
let topScores = [];
for (const [index, score] of scores.entries()) {
if (score == highestScore) {
topScores.push(index);
}
}
console.log(topScores);
the .indexOf() returns only the index of the first occurrence of the particular ignore the other indexes so in this case, indexOf won't work. Just loop through and push the index of the value equal to the maximum number to a new array
let scores = [60, 50, 60, 58, 54, 54, 58, 50, 52, 54, 48, 69, 34, 55, 51, 52, 44, 51, 69, 64, 66, 55, 52, 61, 46, 31, 57, 52, 44, 18, 41, 53, 55, 61, 51, 44];
let noReplica = Math.max(...scores)
let ret = [];
for (let i = 0, length = scores.length; i < length; i++) {
if (scores[i] === noReplica) {
ret.push(i)
}
}
console.log(ret)
scores.indexOf(score) always returns the first position of score in the scores.
Use following code if you need the index.
for (let i = 0; i < scores.length; i ++) {
let score = scores[i];
if (score == highestScore) {
topScores.push(i);
}
}
console.log(topScores);
I have the following code in a test case on jsPerf:
var arr = [0, 45, 96, 8, 69, 62, 80, 91, 89, 24, 6, 23, 49, 88, 26, 40, 87, 61, 83, 2, 60, 53, 43, 82, 67, 3, 65, 37, 42, 77, 73, 38, 9, 46, 75, 10, 63, 15, 47, 28, 79, 55, 59, 95, 11, 93, 70, 98, 25, 48, 30, 5, 72, 12, 84, 1, 29, 13, 50, 33, 19, 7, 31, 57, 32, 44, 74, 51, 35, 90, 86, 54, 4, 64, 92, 71, 22, 41, 16, 17, 27, 76, 39, 18, 99, 94, 36, 66, 85, 20, 21, 56, 34, 81, 14, 78, 68, 58, 97, 52];
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
function quicksort( arr ) {
if ( arr.length <= 1 )
return arr;
var i = 0,
len = arr.length,
less = [],
greater = [],
random = Math.floor( Math.random() * len ),
pivot = arr[ random ];
arr.remove( random );
for ( ; i < len - 1; i++ ){
if ( arr[ i ] <= pivot )
less.push( arr[ i ] );
else
greater.push( arr[ i ] );
}
return quicksort( less ).concat( pivot, quicksort( greater ) );
};
If you copy that into your console and run quicksort( arr ), you'll see that it correctly returns a sorted array.
But for some reason, in this test case on jsPerf, my quicksort function seems to be returning only a single number ( as can be seen in 'Perparation Code Output' ). It also seems to be running way faster than it probably should.
Anybody ideas into what's going on would be greatly appreciated.
I think the problem is that you're calling that .remove() function on the original array, so it quickly strips it down to nothing. In other words, each initial call to the quicksort function removes an element.
When I make it create a copy of the array first, then it seems to work.