Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0.
A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters.
For example, "ace" is a subsequence of "abcde".
A common subsequence of two strings is a subsequence that is common to both strings.
Example 1:
Input:
text1 = "abcde", text2 = "ace"
Output:
3
Explanation:
The longest common subsequence is "ace" and its length is 3.
Example 2:
Input:
text1 = "abc", text2 = "abc"
Output:
3
Explanation:
The longest common subsequence is "abc" and its length is 3.
Example 3:
Input:
text1 = "abc", text2 = "def"
Output:
0
Explanation:
There is no such common subsequence, so the result is 0.
Constraints:
1 <= text1.length, text2.length <= 1000
text1 and text2 consist of only lowercase English characters.
This problem was taken from Leetcode Longest Common Subsequence
Solution
Dynamic programming with memoization.
We create a matrix to store (memoize) the response from previous calculations so we won’t re-calculate them again.
We are starting from the first row comparing every character from column 1 with the first character from row one.
There are two cases:
either the character match, then we add 1 and add it with the value from the diagonal to the left of the cell where we are.
Longest Common Substring Step 1
the characters don’t match, then we get the MAX of the value above current cell and the value to the left of the current cell.
Longest Common Substring Step 2
Here again c int the column (first string) matches c in the row (the second string)
so we get the value of last comparison (to the left and up diagonal of the current cell) which is 1 and add 1 again since characters match.
Longest Common Substring 3
and keep going till we reach the end of both strings which is the answer.
Longest Common Substring 4
/**
* @param {string} text1
* @param {string} text2
* @return {number}
*/
var longestCommonSubsequence = function(text1, text2) {
var txt1 = text1.split('');
var txt2 = text2.split('');
txt1.unshift('0');
txt2.unshift('1');
var l1 = txt1.length;
var l2 = txt2.length;
var matrix = [];
for(var i = 0; i < l2; i ++) {
matrix[i] = new Array(l1).fill(0);
}
var maxCommon = 0;
for(var row = 0; row < l2; row ++) {
for(var col = 0; col < l1; col ++) {
var last = 0;
if(txt1[col] == txt2[row]) {
var previousDiagonalRowVal = row == 0 || col == 0 ? 0 : matrix[row - 1][col - 1];
last = 1 + previousDiagonalRowVal;
}
else {
var prevUp = row == 0 ? 0 : matrix[row - 1][col];
var prevLeft = col == 0 ? 0 : matrix[row][col - 1];
last = Math.max(prevUp, prevLeft);
}
matrix[row][col] = last;
maxCommon = last > maxCommon ? last : maxCommon;
}
}
return maxCommon;
};
var text1 = "abcde", text2 = "ace";
var r = longestCommonSubsequence(text1, text2);
console.log(">>", r);
A linked list of length n is given such that each node contains an additional random pointer, which could point to any node in the list, or null.
Construct a deep copy of the list. The deep copy should consist of exactly nbrand new nodes, where each new node has its value set to the value of its corresponding original node. Both the next and random pointer of the new nodes should point to new nodes in the copied list such that the pointers in the original list and copied list represent the same list state. None of the pointers in the new list should point to nodes in the original list.
For example, if there are two nodes X and Y in the original list, where X.random --> Y, then for the corresponding two nodes x and y in the copied list, x.random --> y.
Return the head of the copied linked list.
The linked list is represented in the input/output as a list of n nodes. Each node is represented as a pair of [val, random_index] where:
val: an integer representing Node.val
random_index: the index of the node (range from 0 to n-1) that the random pointer points to, or null if it does not point to any node.
Your code will only be given the head of the original linked list.
Example 1:
Input:
head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
Output:
[[7,null],[13,0],[11,4],[10,2],[1,0]]
Example 2:
Input:
head = [[1,1],[2,1]]
Output:
[[1,1],[2,1]]
Example 3:
Input:
head = [[3,null],[3,0],[3,null]]
Output:
[[3,null],[3,0],[3,null]]
Constraints:
0 <= n <= 1000
-104 <= Node.val <= 104
Node.random is null or is pointing to some node in the linked list.
/**
* // Definition for a Node.
* function Node(val, next, random) {
* this.val = val;
* this.next = next;
* this.random = random;
* };
*/
/**
* @param {Node} head
* @return {Node}
*/
var copyRandomList = function(head) {
function dfsTraverse(node, visited={}) {
if(!node) {
return null;
}
// if a new node is created, return it. Otherwise you will fall into circular loops
if(node?.clone) {
return node?.clone;
}
var newNode = new Node(node?.val, null, null);
node.clone = newNode;
var next = dfsTraverse(node?.next);
var random = dfsTraverse(node?.random);
newNode.next = next;
newNode.random = random;
return newNode;
}
var result = dfsTraverse(head);
return result;
};
function Node(val, next, random) {
this.val = val;
this.next = next;
this.random = random;
}
;// [1,null],[2,0],[3,1]
var nodes = {};
nodes['1'] = new Node(1,null,null);
nodes['2'] = new Node(2,null,null);
nodes['3'] = new Node(3,null,null);
nodes['1'].next = nodes['2'];
nodes['1'].random = null;
nodes['2'].next = nodes['3'];
nodes['2'].random = nodes['1'];
nodes['3'].next = null;
nodes['3'].random = nodes['2'];
//console.log("root");
//console.log(nodes['7']);
var result = copyRandomList(nodes['1']);
console.log(result);
More elegant solution
/**
* // Definition for a Node.
* function Node(val, next, random) {
* this.val = val;
* this.next = next;
* this.random = random;
* };
*/
/**
* @param {Node} head
* @return {Node}
*/
var copyRandomList = function(head) {
let cur = head;
const copy = new Map();
// add all new nodes and values for now
while (cur) {
copy.set(cur, new Node(cur.val));
cur = cur.next;
}
cur = head;
// iterate again and point curent node to the newly created nodes using the key
while (cur) {
copy.get(cur).next = copy.get(cur.next) || null;
copy.get(cur).random = copy.get(cur.random) || null;
cur = cur.next;
}
return copy.get(head);
}
function Node(val, next, random) {
this.val = val;
this.next = next;
this.random = random;
};
// [1,null],[2,0],[3,1]
var nodes = {};
nodes['1'] = new Node(1,null,null);
nodes['2'] = new Node(2,null,null);
nodes['3'] = new Node(3,null,null);
nodes['1'].next = nodes['2'];
nodes['1'].random = null;
nodes['2'].next = nodes['3'];
nodes['2'].random = nodes['1'];
nodes['3'].next = null;
nodes['3'].random = nodes['2'];
var result = copyRandomList(nodes['1']);
console.log(result);
Given the root node of a binary search tree and two integers low and high, return the sum of values of all nodes with a value in the inclusive range [low, high].
Example 1:
Input:
root = [10,5,15,3,7,null,18], low = 7, high = 15
Output:
32
Explanation:
Nodes 7, 10, and 15 are in the range [7, 15]. 7 + 10 + 15 = 32.
Example 2:
Input:
root = [10,5,15,3,7,13,18,1,null,6], low = 6, high = 10
Output:
23
Explanation:
Nodes 6, 7, and 10 are in the range [6, 10]. 6 + 7 + 10 = 23.
Constraints:
The number of nodes in the tree is in the range [1, 2 * 104].
The graph represent connections between airports. Nodes (vertices) represent airports, and edges represent flights.
BFS search (Breadth First Search)
Breadth-first Search (BFS) starts by pushing all of the direct children to a queue (first-in, first-out). It then visits each item in queue and adds the next layer of children to the back of the queue. Since one node could be visited multiple times causing infinite loop, we have to keep track of all visited nodes.
Using JavaScript Map
// DATA
const airports = 'PHX BKK OKC JFK LAX MEX EZE HEL LOS LAP LIM'.split(' ');
const routes = [
['PHX', 'LAX'],
['PHX', 'JFK'],
['JFK', 'OKC'],
['JFK', 'HEL'],
['JFK', 'LOS'],
['MEX', 'LAX'],
['MEX', 'BKK'],
['MEX', 'LIM'],
['MEX', 'EZE'],
['LIM', 'BKK'],
];
// The graph
const adjacencyList = new Map();
// Add node
function addNode(airport) {
adjacencyList.set(airport, []);
}
// Add edge, undirected
function addEdge(origin, destination) {
adjacencyList.get(origin).push(destination);
adjacencyList.get(destination).push(origin);
}
// Create the Graph
airports.forEach(addNode);
routes.forEach(route => addEdge(route[0], route[1]));
console.log(adjacencyList);
function bfs(start, searchItem) {
const visited = new Set();
const queue = [start];
while (queue.length > 0) {
const airport = queue.shift(); // mutates the queue
const destinations = adjacencyList.get(airport);
for (const destination of destinations) {
if (destination === searchItem) {
console.log(`BFS found a route to`, searchItem)
}
if (!visited.has(destination)) {
visited.add(destination);
queue.push(destination);
}
}
}
}
bfs('PHX', 'BKK');
Using JavaScript object
// Data
var airports = {
'PHX': ['LAX', 'JFK'],
'BKK': ['MEX', 'LIM'],
'OKC': ['JFK'],
'JFK': ['LAX', 'PHX', 'OKC', 'HEL', 'LOS', 'EZE'],
'LAX': ['PHX', 'JFK', 'MEX'],
'MEX': ['LAX', 'EZE', 'BKK', 'LIM'],
'EZE': ['JFK', 'MEX'],
'HEL': ['JFK'],
'LOS': ['JFK'],
'LAP': [],
'LIM': ['MEX', 'BKK']
};
function bfs(start, endDest) {
var queue = [start];
var visited = {};
while(queue.length > 0) {
var curentNodeVal = queue.shift();
var childNodes = airports[curentNodeVal];
for(const childNode of childNodes) {
if(childNode === endDest) {
console.log("BFS found a route to :", endDest);
}
if(!visited[childNode]) {
console.log(childNode);
visited[childNode] = true;
queue.push( childNode);
}
}
}
}
bfs('PHX', 'BKK');
DFS (Depth First Search)
Depth-first Search (DFS) will go as far into the graph as possible until it reaches a node without any children, at which point it backtracks and continues the process. The algorithm can be implemented with a recursive function that keeps track of previously visited nodes. If a node has not been visited, we call the function recursively.
Using JavaScript Map
// DATA
const airports = 'PHX BKK OKC JFK LAX MEX EZE HEL LOS LAP LIM'.split(' ');
const routes = [
['PHX', 'LAX'],
['PHX', 'JFK'],
['JFK', 'OKC'],
['JFK', 'HEL'],
['JFK', 'LOS'],
['MEX', 'LAX'],
['MEX', 'BKK'],
['MEX', 'LIM'],
['MEX', 'EZE'],
['LIM', 'BKK'],
];
// The graph
const adjacencyList = new Map();
// Add node
function addNode(airport) {
adjacencyList.set(airport, []);
}
// Add edge, undirected
function addEdge(origin, destination) {
adjacencyList.get(origin).push(destination);
adjacencyList.get(destination).push(origin);
}
// Create the Graph
airports.forEach(addNode);
routes.forEach(route => addEdge(route[0], route[1]));
console.log(adjacencyList);
function dfs(start, visited = new Set()) {
console.log(start)
visited.add(start);
const destinations = adjacencyList.get(start);
for (const destination of destinations) {
if (destination === 'BKK') {
console.log(`DFS found Bangkok`)
return;
}
if (!visited.has(destination)) {
dfs(destination, visited);
}
}
}
dfs('PHX');
Straight forward, but we have to consider negative ranges.
/**
* @param {number} x
* @param {number} n
* @return {number}
*/
var myPow = function(x, n) {
if(n === 0) {
return 1;
}
var N = n;
var X = x;
var result = 1;
if(n < 0) {
X = 1 / x;
N = -n;
}
for(var i = 0; i < N; i ++) {
result = result * X;
}
return result;
};
Approach 2: Fast Power Algorithm Recursive
divide n so you immediately cut the computation time in half to logarithmic one.
/**
* @param {number} x
* @param {number} n
* @return {number}
*/
var myPow = function(x, n) {
function fastPow(x, n) {
if(n < 1) {
return 1;
}
var half = fastPow(x, Math.floor(n / 2));
if(n % 2 == 0) {
return half * half;
}
else {
return half * half * x;
}
}
var X = x;
var N = n;
if(n < 0) {
X = 1 / x;
N = -n;
}
return fastPow(X, N);
};
According MDN all three methods are very similar, and at the end they produce very similar result.
they all accept this object as first parameter.
then apply accepts and array of arguments vs bind and call accept a sequence of arguments.
Function.prototype.apply()
The apply() method calls a function with a given this value, and arguments provided as an array
Function.prototype.bind()
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called