In English, we have a concept called root, which can be followed by some other word to form another longer word – let’s call this word successor. For example, when the root"an" is followed by the successor word "other", we can form a new word "another".
Given a dictionary consisting of many roots and a sentence consisting of words separated by spaces, replace all the successors in the sentence with the root forming it. If a successor can be replaced by more than one root, replace it with the root that has the shortest length.
Return the sentence after the replacement.
Example 1:
Input: dictionary = ["cat","bat","rat"], sentence = "the cattle was rattled by the battery"
Output: "the cat was rat by the bat"
Example 2:
Input: dictionary = ["a","b","c"], sentence = "aadsfasf absbs bbab cadsfafs"
Output: "a a b c"
Example 3:
Input: dictionary = ["a", "aa", "aaa", "aaaa"], sentence = "a aa a aaaa aaa aaa aaa aaaaaa bbb baba ababa"
Output: "a a a a a a a a bbb baba a"
Example 4:
Input: dictionary = ["catt","cat","bat","rat"], sentence = "the cattle was rattled by the battery"
Output: "the cat was rat by the bat"
Example 5:
Input: dictionary = ["ac","ab"], sentence = "it is abnormal that this solution is accepted"
Output: "it is ab that this solution is ac"
Constraints:
1 <= dictionary.length <= 1000
1 <= dictionary[i].length <= 100
dictionary[i] consists of only lower-case letters.
1 <= sentence.length <= 10^6
sentence consists of only lower-case letters and spaces.
The number of words in sentence is in the range [1, 1000]
The length of each word in sentence is in the range [1, 1000]
Each two consecutive words in sentence will be separated by exactly one space.
sentence does not have leading or trailing spaces.
Javascript Solution:
/**
* @param {string[]} dictionary
* @param {string} sentence
* @return {string}
*/
var replaceWords = function(dictionary, sentence) {
let word = "";
let output = "";
let found = false;
sentence += " "; // at the end, add the last word
for (let c of sentence) {
if (c === " ") {
if (output.length !== 0) output += " ";
output += word;
word = "";
found = false;
continue;
}
if (found) continue;
word += c;
if (dictionary.includes(word)) {
found = true;
}
}
return output;
};
Java Solution (Trie):
class Solution {
public String replaceWords(List<String> roots, String sentence) {
TrieNode trie = new TrieNode();
for (String root : roots) {
TrieNode cur = trie;
for (char letter : root.toCharArray()) {
if (cur.children[letter - 'a'] == null) {
cur.children[letter - 'a'] = new TrieNode();
}
cur = cur.children[letter - 'a'];
}
cur.word = root;
}
StringBuilder ans = new StringBuilder();
for (String word : sentence.split(" ")) {
if (ans.length() > 0) {
ans.append(" ");
}
TrieNode cur = trie;
for (char letter : word.toCharArray()) {
if (cur.children[letter - 'a'] == null || cur.word != null) {
break;
}
cur = cur.children[letter - 'a'];
}
ans.append(cur.word != null ? cur.word : word);
}
return ans.toString();
}
}
class TrieNode {
TrieNode[] children;
String word;
TrieNode() {
children = new TrieNode[26];
}
}
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function(nums) {
let res = [];
let cur = [];
let len = nums.length;
let used = new Array(len).fill(false);
dfs(nums, len, res, cur, used);
return res;
}
function dfs(nums, len, res, cur, used) {
// exit recursive condition
if (cur.length === len) {
res.push(cur.concat());
return;
}
// possible solution
for (let i = 0; i < len; i++) {
// skip used integer
if (used[i]) continue;
// modify current state
used[i] = true;
cur.push(nums[i]);
dfs(nums, len, res, cur, used);
// recover current state
cur.pop();
used[i] = false;
}
}
Use on Binary Tree, Permutation or Combination problems
Time complexity:
Tree traversal: O(n)
Permutation: O(n! * n)
Combination: O(2n * n)
function dfs(nums, n, start, cur, res...) {
if (exit recursive condition) {
// record answer
return;
}
for (possible solution) {
// modify current state
dfs(nums, n, i + 1, cur, res...);
// recover current state
}
return something if need // option
};
BFS Template
Use on Topological Sorting, Level Traversal, Shortest path in graph, Union Find problems
Time complexity:O(n + m) // n is node, m is edge
Space complexity: O(n)
// 1. No level:
function bfs(node) {
const queue = [];
queue.push(node);
while(queue.length !== 0) {
...
let cur = queue.shift();
for (neighbor in cur\'s neighbors) {
if (neighbor is valid but not visit)
queue.push(neighbor)
}
}
}
// 2. Has level traversal: depends on the questions
// Level could be a number or array
function bfs(node) {
const queue = [];
queue.push(node);
// (1) let level = 0; // level as a number for tracking
while(queue.length !== 0) {
let size = queue.length;
// (2) let level = []; // level could be storing data
while(size--) {
let cur = queue.shift();
for (neighbor in cur\'s neighbors) {
if (neighbor is valid but not visit)
queue.push(neighbor)
}
}
//(1) level++;
//(2) res.push(level.concat()); // adding to the result
}
}
// 3. BFS - finding the shortest path in graph
function bfs(node) {
const queue = [];
let distance = new Map(); // avoid duplicate, track startNode
queue.push(startNode);
distance.set(startNode, 0); // or 1 depends on the question
while(queue.length !== 0) {
let cur = queue.shift();
if (node is destination) {
break or return something;
}
for (neighbor in cur\'s neighbors) {
if (distance.has(neighbor)) continue;
queue.push(neighbor);
distance.set(neighbor, distance.get(node) + 1);
}
}
// possible return:
return distance;
return distance.keys();
return distnace.get(endNode);
...
}
Topological Sorting (BFS) Template:
function topologicalSort(nodes) {
const queue = [];
let indegrees = getIndegrees(nodes); // 统计所有点的入度信息 as hashmap
// add those indegree === 0 nodes into queue
for (node of nodes) {
if (indegrees.get(node) === 0)
queue.push(node);
}
let topoOrder = [];
while(queue.length !== 0) {
let cur = queue.shift();
topoOrder.push(cur);
for (neighbor in cur\'s neighbors) {
indegrees.set(neighbor, indegrees.get(neighbor) - 1);
// when indegree is 0, no more depends on any node, add to queue
if (indegrees.get(neighbor) === 0) {
queue.push(neighbor);
}
}
if (topoOrder.length !== nodes.length)
return "No topoOrder!";
return topoOrder;
}
Binary Search Template
Time complexity O(logn)
Space complexity O(1)
function binarySearch(nums, target) {
let l = 0;
let r = nums.length;
while (l < r) {
let mid = l + Math.floor((r - l) / 2);
if (nums[mid] === target) {
// most case: just return target
return mid;
// --- if searching leftBound
// leftBound = mid;
// r = mid;
// --- if searching rightBound
// rightBound = mid;
// l = mid + 1;
} else if (nums[mid] > target) {
r = mid;
} else {
l = mid + 1;
}
}
}
Merge Sort Template
var sortArray = function(nums) {
if (nums === null || nums.length === 0) return;
let temp = [];
mergeSort(nums, 0, nums.length - 1, temp);
return nums;
};
function mergeSort(nums, start, end, temp) {
if (start >= end) return;
// deal with first half
mergeSort(nums, start, Math.floor((start + end) / 2), temp);
// deal with second half
mergeSort(nums, Math.floor((start +end) / 2) + 1, end, temp);
// merge
merge(nums, start, end, temp);
}
function merge(nums, start, end, temp) {
let mid = Math.floor((start + end) / 2);
let l_index = start;
let r_index = mid + 1;
let index = start;
while (l_index <= mid && r_index <= end) {
if (nums[l_index] < nums[r_index]) {
temp[index++] = nums[l_index++];
} else {
temp[index++] = nums[r_index++];
}
}
while (l_index <= mid)
temp[index++] = nums[l_index++];
while (r_index <= end)
temp[index++] = nums[r_index++];
for (let i = start; i <= end; i++) {
nums[i] = temp[i];
}
}
Quick Sort Template
var sortArray = function(nums) {
if (nums === null || nums.length === 0) return;
quickSort(nums, 0, nums.length - 1)
return nums;
};
function quickSort(nums, left, right) {
let index;
if (nums.length > 1) {
// index returned from partition
index = partition(nums, left, right);
// more elements on the left side of the pivot
if (left < index - 1)
quickSort(nums, left, index - 1);
// more elements on the right side of the pivot
if (index < right)
quickSort(nums, index, right);
}
// you can return or not return,
// since we change nums, no need to return
// return nums;
}
function partition(nums, start, end) {
let l = start, r = end;
let mid = Math.floor((start + end) / 2);
let pivot = nums[mid];
// Important: l <= r not l < r
while (l <= r) {
while(nums[l] < pivot)
l++;
while(nums[r] > pivot)
r--;
if (l <= r) {
// swap
[nums[l], nums[r]] = [nums[r], nums[l]];
l++;
r--;
}
}
return l;
}
Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sum to target.
Each number in candidates may only be used once in the combination.
Note: The solution set must not contain duplicate combinations.
Create a DFS recursive and keep tracking target value and start index
Exit DFS recursive:
if (target < 0) return;
if (target === 0) record answer and return;
For loop (Possible solution) pay attention to:
No duplication
Candidates can only be used once
Solution:
/**
* @param {number[]} candidates
* @param {number} target
* @return {number[][]}
*/
var combinationSum2 = function(candidates, target) {
let res = [];
let cur = [];
// Important Step: sort the array
candidates.sort();
dfs(res, cur, 0, candidates, target);
return res;
}
function dfs(res, cur, start, can, target) {
// exit recursive condition
if (target < 0) return;
if (target === 0) {
res.push(cur.concat());
return;
}
for (let i = start; i < can.length; i++) {
// skip duplicate
if (i > start && can[i] === can[i - 1]) continue;
cur.push(can[i]);
// Each number in candidates may only be used once
// in the combination. start from i + 1
dfs(res, cur, i + 1, can, target - can[i]);
cur.pop();
}
}
Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*' where:
'.' Matches any single character.
'*' Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
Example 1:
Input: s = "aa", p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".
Example 2:
Input: s = "aa", p = "a*"
Output: true
Explanation: '*' means zero or more of the preceding element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".
Example 3:
Input: s = "ab", p = ".*"
Output: true
Explanation: ".*" means "zero or more (*) of any character (.)".
Example 4:
Input: s = "aab", p = "c*a*b"
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches "aab".
Example 5:
Input: s = "mississippi", p = "mis*is*p*."
Output: false
Constraints:
0 <= s.length <= 20
0 <= p.length <= 30
s contains only lowercase English letters.
p contains only lowercase English letters, '.', and '*'.
It is guaranteed for each appearance of the character '*', there will be a previous valid character to match.
Idea:
Backtracking / Depth-first search (DFS)
There are two normal cases without consideration of *:
1. s === p 2. s !== p, but p === '.' and s is any character
And then let’s consider how to handle the pattern *:
If we have a x* in the pattern(x stands for any character), we may ignore this part of the pattern, or delete a matching character in the text
// no match: aa , c* || match: aaa , a*
(isMatch(s, p.substring(2)) || (first_match && isMatch(s.substring(1), p)));
According to these analyses, we can construct a depth-first search algorithm, it’s a recursive algorithm.
Solution:
/**
* @param {string} s
* @param {string} p
* @return {boolean}
*/
var isMatch = function(s, p) {
let sLen = s.length;
let pLen = p.length;
if (pLen === 0) return sLen === 0;
let first_match = (sLen !== 0 && (p[0] === s[0] || p[0] === '.'));
// If a star is present in the pattern, it will be in the
// second position p[1]. Then, we may ignore this part of
// the pattern, or delete a matching character in the text.
// If we have a match on the remaining strings after any of
// these operations, then the initial inputs matched.
if (pLen >= 2 && p[1] === '*') {
// no match: aa , c* || match: aaa , a*
return (isMatch(s, p.substring(2)) || (first_match && isMatch(s.substring(1), p)));
} else {
return first_match && isMatch(s.substring(1), p.substring(1));
}
};
Given a m * n matrix of ones and zeros, return how many square submatrices have all ones.
Example 1:
Input: matrix =
[
[0,1,1,1],
[1,1,1,1],
[0,1,1,1]
]
Output: 15
Explanation:
There are 10 squares of side 1.
There are 4 squares of side 2.
There is 1 square of side 3.
Total number of squares = 10 + 4 + 1 = 15.
Example 2:
Input: matrix =
[
[1,0,1],
[1,1,0],
[1,1,0]
]
Output: 7
Explanation:
There are 6 squares of side 1.
There is 1 square of side 2.
Total number of squares = 6 + 1 = 7.
Constraints:
1 <= arr.length <= 300
1 <= arr[0].length <= 300
0 <= arr[i][j] <= 1
Idea:
Dynamic Programing
dp[i][j] := edge of largest square with bottom right corner at (i, j)
base case:
dp[0][0], dp[i][0], dp[0][j] = 1 if martix[i][j] = 1
The current unit can form the largest square matrix with the left, upper and upper left units. For example, a unit can form a matrix with a side length of 3 with the left, top, and top left units. That can also form a matrix with a side length of 2 and a side length of 1. So: