diff --git a/articles/design-a-food-rating-system.md b/articles/design-a-food-rating-system.md new file mode 100644 index 000000000..7be2642c1 --- /dev/null +++ b/articles/design-a-food-rating-system.md @@ -0,0 +1,457 @@ +## 1. Brute Force + +::tabs-start + +```python +class FoodRatings: + + def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]): + self.foodToRating = {} # food -> rating + self.cuisineToFood = defaultdict(list) # cuisine -> [food] + for i in range(len(foods)): + self.foodToRating[foods[i]] = ratings[i] + self.cuisineToFood[cuisines[i]].append(foods[i]) + + def changeRating(self, food: str, newRating: int) -> None: + self.foodToRating[food] = newRating + + def highestRated(self, cuisine: str) -> str: + maxR, res = 0, "" + for food in self.cuisineToFood[cuisine]: + r = self.foodToRating[food] + if r > maxR or (r == maxR and food < res): + res = food + maxR = r + return res +``` + +```java +public class FoodRatings { + private Map foodToRating; + private Map> cuisineToFood; + + public FoodRatings(String[] foods, String[] cuisines, int[] ratings) { + foodToRating = new HashMap<>(); + cuisineToFood = new HashMap<>(); + for (int i = 0; i < foods.length; i++) { + foodToRating.put(foods[i], ratings[i]); + cuisineToFood.computeIfAbsent(cuisines[i], k -> new ArrayList<>()).add(foods[i]); + } + } + + public void changeRating(String food, int newRating) { + foodToRating.put(food, newRating); + } + + public String highestRated(String cuisine) { + int maxR = 0; + String res = ""; + for (String food : cuisineToFood.get(cuisine)) { + int r = foodToRating.get(food); + if (r > maxR || (r == maxR && food.compareTo(res) < 0)) { + res = food; + maxR = r; + } + } + return res; + } +} +``` + +```cpp +class FoodRatings { +private: + unordered_map foodToRating; + unordered_map> cuisineToFood; + +public: + FoodRatings(vector& foods, vector& cuisines, vector& ratings) { + for (size_t i = 0; i < foods.size(); i++) { + foodToRating[foods[i]] = ratings[i]; + cuisineToFood[cuisines[i]].push_back(foods[i]); + } + } + + void changeRating(string food, int newRating) { + foodToRating[food] = newRating; + } + + string highestRated(string cuisine) { + int maxR = 0; + string res = ""; + for (const string& food : cuisineToFood[cuisine]) { + int r = foodToRating[food]; + if (r > maxR || (r == maxR && food < res)) { + res = food; + maxR = r; + } + } + return res; + } +}; +``` + +```javascript +class FoodRatings { + /** + * @param {string[]} foods + * @param {string[]} cuisines + * @param {number[]} ratings + */ + constructor(foods, cuisines, ratings) { + this.foodToRating = new Map(); + this.cuisineToFood = new Map(); + + for (let i = 0; i < foods.length; i++) { + this.foodToRating.set(foods[i], ratings[i]); + if (!this.cuisineToFood.has(cuisines[i])) { + this.cuisineToFood.set(cuisines[i], []); + } + this.cuisineToFood.get(cuisines[i]).push(foods[i]); + } + } + + /** + * @param {string} food + * @param {number} newRating + * @return {void} + */ + changeRating(food, newRating) { + this.foodToRating.set(food, newRating); + } + + /** + * @param {string} cuisine + * @return {string} + */ + highestRated(cuisine) { + let maxR = 0, res = ""; + for (let food of this.cuisineToFood.get(cuisine)) { + let r = this.foodToRating.get(food); + if (r > maxR || (r === maxR && food < res)) { + res = food; + maxR = r; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $changeRating()$ function call. + * $O(n)$ time for each $highestRated()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Heap + +::tabs-start + +```python +class FoodRatings: + + def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]): + self.foodToRating = {} # food -> rating + self.foodToCuisine = {} # food -> cuisine + self.cuisineToHeap = defaultdict(list) # cuisine -> max_heap + + for i in range(len(foods)): + self.foodToRating[foods[i]] = ratings[i] + self.foodToCuisine[foods[i]] = cuisines[i] + heappush(self.cuisineToHeap[cuisines[i]], (-ratings[i], foods[i])) + + def changeRating(self, food: str, newRating: int) -> None: + cuisine = self.foodToCuisine[food] + self.foodToRating[food] = newRating + heappush(self.cuisineToHeap[cuisine], (-newRating, food)) + + def highestRated(self, cuisine: str) -> str: + heap = self.cuisineToHeap[cuisine] + while heap: + rating, food = heap[0] + if -rating == self.foodToRating[food]: + return food + heappop(heap) +``` + +```java +public class FoodRatings { + private Map foodToRating; + private Map foodToCuisine; + private Map> cuisineToHeap; + + private static class Food { + int rating; + String name; + + Food(int rating, String name) { + this.rating = rating; + this.name = name; + } + } + + public FoodRatings(String[] foods, String[] cuisines, int[] ratings) { + foodToRating = new HashMap<>(); + foodToCuisine = new HashMap<>(); + cuisineToHeap = new HashMap<>(); + + for (int i = 0; i < foods.length; i++) { + foodToRating.put(foods[i], ratings[i]); + foodToCuisine.put(foods[i], cuisines[i]); + cuisineToHeap + .computeIfAbsent(cuisines[i], k -> new PriorityQueue<>( + (a, b) -> a.rating == b.rating ? a.name.compareTo(b.name) : b.rating - a.rating)) + .offer(new Food(ratings[i], foods[i])); + } + } + + public void changeRating(String food, int newRating) { + String cuisine = foodToCuisine.get(food); + foodToRating.put(food, newRating); + cuisineToHeap.get(cuisine).offer(new Food(newRating, food)); + } + + public String highestRated(String cuisine) { + PriorityQueue heap = cuisineToHeap.get(cuisine); + while (!heap.isEmpty()) { + Food top = heap.peek(); + if (foodToRating.get(top.name) == top.rating) { + return top.name; + } + heap.poll(); + } + return ""; + } +} +``` + +```cpp +class FoodRatings { + unordered_map foodToRating; + unordered_map foodToCuisine; + struct cmp { + bool operator()(const pair& a, const pair& b) { + if (a.first == b.first) return a.second > b.second; + return a.first < b.first; + } + }; + unordered_map, + vector>, cmp>> cuisineToHeap; + +public: + FoodRatings(vector& foods, vector& cuisines, vector& ratings) { + for (int i = 0; i < foods.size(); i++) { + foodToRating[foods[i]] = ratings[i]; + foodToCuisine[foods[i]] = cuisines[i]; + cuisineToHeap[cuisines[i]].push({ratings[i], foods[i]}); + } + } + + void changeRating(string food, int newRating) { + string cuisine = foodToCuisine[food]; + foodToRating[food] = newRating; + cuisineToHeap[cuisine].push({newRating, food}); + } + + string highestRated(string cuisine) { + auto &heap = cuisineToHeap[cuisine]; + while (!heap.empty()) { + auto [rating, food] = heap.top(); + if (foodToRating[food] == rating) return food; + heap.pop(); + } + return ""; + } +}; +``` + +```javascript +class FoodRatings { + /** + * @param {string[]} foods + * @param {string[]} cuisines + * @param {number[]} ratings + */ + constructor(foods, cuisines, ratings) { + this.foodToRating = new Map(); + this.foodToCuisine = new Map(); + this.cuisineToHeap = new Map(); + + for (let i = 0; i < foods.length; i++) { + this.foodToRating.set(foods[i], ratings[i]); + this.foodToCuisine.set(foods[i], cuisines[i]); + if (!this.cuisineToHeap.has(cuisines[i])) { + this.cuisineToHeap.set(cuisines[i], new PriorityQueue( + (a, b) => b.rating - a.rating || a.name.localeCompare(b.name) + )); + } + this.cuisineToHeap.get(cuisines[i]).enqueue( + { rating: ratings[i], name: foods[i] } + ); + } + } + + /** + * @param {string} food + * @param {number} newRating + * @return {void} + */ + changeRating(food, newRating) { + let cuisine = this.foodToCuisine.get(food); + this.foodToRating.set(food, newRating); + this.cuisineToHeap.get(cuisine).enqueue( + { rating: newRating, name: food } + ); + } + + /** + * @param {string} cuisine + * @return {string} + */ + highestRated(cuisine) { + let heap = this.cuisineToHeap.get(cuisine); + while (!heap.isEmpty()) { + let top = heap.front(); + if (this.foodToRating.get(top.name) === top.rating) { + return top.name; + } + heap.dequeue(); + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n \log n)$ time for initialization. + * $O(\log n)$ time for each $changeRating()$ function call. + * $O(\log n)$ time for each $highestRated()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Sorted Set + +::tabs-start + +```python +class FoodRatings: + + def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]): + self.foodToRating = {} # food -> rating + self.foodToCuisine = {} # food -> cuisine + self.cuisineToSortedSet = defaultdict(SortedSet) # cuisine -> SortedSet[(rating, food)] + + for i in range(len(foods)): + self.foodToRating[foods[i]] = ratings[i] + self.foodToCuisine[foods[i]] = cuisines[i] + self.cuisineToSortedSet[cuisines[i]].add((-ratings[i], foods[i])) + + def changeRating(self, food: str, newRating: int) -> None: + cuisine = self.foodToCuisine[food] + oldRating = self.foodToRating[food] + + self.cuisineToSortedSet[cuisine].remove((-oldRating, food)) + self.foodToRating[food] = newRating + self.cuisineToSortedSet[cuisine].add((-newRating, food)) + + def highestRated(self, cuisine: str) -> str: + return self.cuisineToSortedSet[cuisine][0][1] +``` + +```java +public class FoodRatings { + private Map foodToRating; + private Map foodToCuisine; + private Map> cuisineToSortedSet; + + private static class FoodPair { + int rating; + String food; + + FoodPair(int rating, String food) { + this.rating = rating; + this.food = food; + } + } + + public FoodRatings(String[] foods, String[] cuisines, int[] ratings) { + foodToRating = new HashMap<>(); + foodToCuisine = new HashMap<>(); + cuisineToSortedSet = new HashMap<>(); + + for (int i = 0; i < foods.length; i++) { + foodToRating.put(foods[i], ratings[i]); + foodToCuisine.put(foods[i], cuisines[i]); + cuisineToSortedSet.computeIfAbsent(cuisines[i], k -> new TreeSet<>((a, b) -> { + if (a.rating != b.rating) return b.rating - a.rating; + return a.food.compareTo(b.food); + })).add(new FoodPair(ratings[i], foods[i])); + } + } + + public void changeRating(String food, int newRating) { + String cuisine = foodToCuisine.get(food); + int oldRating = foodToRating.get(food); + TreeSet set = cuisineToSortedSet.get(cuisine); + set.remove(new FoodPair(oldRating, food)); + foodToRating.put(food, newRating); + set.add(new FoodPair(newRating, food)); + } + + public String highestRated(String cuisine) { + return cuisineToSortedSet.get(cuisine).first().food; + } +} +``` + +```cpp +class FoodRatings { + unordered_map foodToRating; + unordered_map foodToCuisine; + unordered_map>> cuisineToSet; + +public: + FoodRatings(vector& foods, vector& cuisines, vector& ratings) { + for (int i = 0; i < foods.size(); i++) { + foodToRating[foods[i]] = ratings[i]; + foodToCuisine[foods[i]] = cuisines[i]; + cuisineToSet[cuisines[i]].insert({-ratings[i], foods[i]}); + } + } + + void changeRating(string food, int newRating) { + string cuisine = foodToCuisine[food]; + auto& s = cuisineToSet[cuisine]; + + s.erase({-foodToRating[food], food}); + foodToRating[food] = newRating; + s.insert({-newRating, food}); + } + + string highestRated(string cuisine) { + return begin(cuisineToSet[cuisine])->second; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n \log n)$ time for initialization. + * $O(\log n)$ time for each $changeRating()$ function call. + * $O(1)$ in Python and $O(\log n)$ in other languages for each $highestRated()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/design-parking-system.md b/articles/design-parking-system.md new file mode 100644 index 000000000..4abdb9aa3 --- /dev/null +++ b/articles/design-parking-system.md @@ -0,0 +1,167 @@ +## 1. Array - I + +::tabs-start + +```python +class ParkingSystem: + + def __init__(self, big: int, medium: int, small: int): + self.spaces = [big, medium, small] + + def addCar(self, carType: int) -> bool: + if self.spaces[carType - 1] > 0: + self.spaces[carType - 1] -= 1 + return True + return False +``` + +```java +public class ParkingSystem { + private int[] spaces; + + public ParkingSystem(int big, int medium, int small) { + spaces = new int[]{big, medium, small}; + } + + public boolean addCar(int carType) { + if (spaces[carType - 1] > 0) { + spaces[carType - 1]--; + return true; + } + return false; + } +} +``` + +```cpp +class ParkingSystem { + int spaces[3]; + +public: + ParkingSystem(int big, int medium, int small) { + spaces[0] = big; + spaces[1] = medium; + spaces[2] = small; + } + + bool addCar(int carType) { + if (spaces[carType - 1] > 0) { + spaces[carType - 1]--; + return true; + } + return false; + } +}; +``` + +```javascript +class ParkingSystem { + /** + * @constructor + * @param {number} big + * @param {number} medium + * @param {number} small + */ + constructor(big, medium, small) { + this.spaces = [big, medium, small]; + } + + /** + * @param {number} carType + * @return {boolean} + */ + addCar(carType) { + if (this.spaces[carType - 1] > 0) { + this.spaces[carType - 1]--; + return true; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $addCar()$ function call. +* Space complexity: $O(1)$ + +--- + +## 2. Array - II + +::tabs-start + +```python +class ParkingSystem: + + def __init__(self, big: int, medium: int, small: int): + self.spaces = [big, medium, small] + + def addCar(self, carType: int) -> bool: + self.spaces[carType - 1] -= 1 + return self.spaces[carType - 1] >= 0 +``` + +```java +public class ParkingSystem { + int[] spaces; + + public ParkingSystem(int big, int medium, int small) { + spaces = new int[]{big, medium, small}; + } + + public boolean addCar(int carType) { + return spaces[carType - 1]-- > 0; + } +} +``` + +```cpp +class ParkingSystem { + int spaces[3]; + +public: + ParkingSystem(int big, int medium, int small) { + spaces[0] = big, spaces[1] = medium, spaces[2] = small; + } + + bool addCar(int carType) { + return spaces[carType - 1]-- > 0; + } +}; +``` + +```javascript +class ParkingSystem { + /** + * @constructor + * @param {number} big + * @param {number} medium + * @param {number} small + */ + constructor(big, medium, small) { + this.spaces = [big, medium, small]; + } + + /** + * @param {number} carType + * @return {boolean} + */ + addCar(carType) { + return this.spaces[carType - 1]-- > 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $addCar()$ function call. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/design-underground-system.md b/articles/design-underground-system.md new file mode 100644 index 000000000..d03c0c9e8 --- /dev/null +++ b/articles/design-underground-system.md @@ -0,0 +1,357 @@ +## 1. Two HashMaps + +::tabs-start + +```python +class UndergroundSystem: + + def __init__(self): + self.checkInMap = {} # id -> (startStation, time) + self.routeMap = {} # (start, end) -> [totalTime, count] + + def checkIn(self, id: int, startStation: str, t: int) -> None: + self.checkInMap[id] = (startStation, t) + + def checkOut(self, id: int, endStation: str, t: int) -> None: + startStation, time = self.checkInMap[id] + route = (startStation, endStation) + if route not in self.routeMap: + self.routeMap[route] = [0, 0] + self.routeMap[route][0] += t - time + self.routeMap[route][1] += 1 + + def getAverageTime(self, startStation: str, endStation: str) -> float: + totalTime, count = self.routeMap[(startStation, endStation)] + return totalTime / count +``` + +```java +public class UndergroundSystem { + private Map> checkInMap; + private Map routeMap; + + public UndergroundSystem() { + checkInMap = new HashMap<>(); + routeMap = new HashMap<>(); + } + + public void checkIn(int id, String startStation, int t) { + checkInMap.put(id, new Pair<>(startStation, t)); + } + + public void checkOut(int id, String endStation, int t) { + Pair entry = checkInMap.get(id); + String route = entry.getKey() + "," + endStation; + routeMap.putIfAbsent(route, new int[]{0, 0}); + routeMap.get(route)[0] += t - entry.getValue(); + routeMap.get(route)[1] += 1; + } + + public double getAverageTime(String startStation, String endStation) { + int[] data = routeMap.get(startStation + "," + endStation); + return (double) data[0] / data[1]; + } +} +``` + +```cpp +class UndergroundSystem { + unordered_map> checkInMap; + unordered_map> routeMap; + +public: + UndergroundSystem() {} + + void checkIn(int id, string startStation, int t) { + checkInMap[id] = {startStation, t}; + } + + void checkOut(int id, string endStation, int t) { + auto [startStation, time] = checkInMap[id]; + string route = startStation + "," + endStation; + if (!routeMap.count(route)) + routeMap[route] = {0, 0}; + routeMap[route].first += t - time; + routeMap[route].second += 1; + } + + double getAverageTime(string startStation, string endStation) { + string route = startStation + "," + endStation; + auto [totalTime, count] = routeMap[route]; + return (double) totalTime / count; + } +}; +``` + +```javascript +class UndergroundSystem { + /** + * @constructor + */ + constructor() { + this.checkInMap = new Map(); + this.routeMap = new Map(); + } + + /** + * @param {number} id + * @param {string} startStation + * @param {number} t + * @return {void} + */ + checkIn(id, startStation, t) { + this.checkInMap.set(id, [startStation, t]); + } + + /** + * @param {number} id + * @param {string} endStation + * @param {number} t + * @return {void} + */ + checkOut(id, endStation, t) { + const [startStation, time] = this.checkInMap.get(id); + const route = `${startStation},${endStation}`; + if (!this.routeMap.has(route)) this.routeMap.set(route, [0, 0]); + this.routeMap.get(route)[0] += t - time; + this.routeMap.get(route)[1] += 1; + } + + /** + * @param {string} startStation + * @param {string} endStation + * @return {number} + */ + getAverageTime(startStation, endStation) { + const [totalTime, count] = this.routeMap.get(`${startStation},${endStation}`); + return totalTime / count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $checkIn()$ function call. + * $O(m)$ time for each $checkOut()$ and $getAverageTime()$ function calls. +* Space complexity: $O(n + N ^ 2)$ + +> Where $n$ is the number of passengers, $N$ is the total number of stations, and $m$ is the average length of station name. + +--- + +## 2. Two HashMaps + Hashing + +::tabs-start + +```python +class UndergroundSystem: + MOD1, MOD2 = 768258391, 685683731 + BASE1, BASE2 = 37, 31 + + def __init__(self): + self.checkInMap = {} # id -> (startStation, time) + self.routeMap = {} # hash(route) -> (totalTime, count) + + def getHash(self, s1: str, s2: str) -> int: + h1, h2, p1, p2 = 0, 0, 1, 1 + for c in s1 + ',' + s2: + h1 = (h1 + (ord(c) - 96) * p1) % self.MOD1 + h2 = (h2 + (ord(c) - 96) * p2) % self.MOD2 + p1 = (p1 * self.BASE1) % self.MOD1 + p2 = (p2 * self.BASE2) % self.MOD2 + return (h1 << 32) | h2 + + def checkIn(self, id: int, startStation: str, t: int) -> None: + self.checkInMap[id] = (startStation, t) + + def checkOut(self, id: int, endStation: str, t: int) -> None: + startStation, time = self.checkInMap[id] + routeHash = self.getHash(startStation, endStation) + if routeHash not in self.routeMap: + self.routeMap[routeHash] = [0, 0] + self.routeMap[routeHash][0] += t - time + self.routeMap[routeHash][1] += 1 + + def getAverageTime(self, startStation: str, endStation: str) -> float: + routeHash = self.getHash(startStation, endStation) + totalTime, count = self.routeMap[routeHash] + return totalTime / count +``` + +```java +public class UndergroundSystem { + private static final int MOD1 = 768258391, MOD2 = 685683731; + private static final int BASE1 = 37, BASE2 = 31; + private Map> checkInMap; + private Map routeMap; + + public UndergroundSystem() { + checkInMap = new HashMap<>(); + routeMap = new HashMap<>(); + } + + private long getHash(String s1, String s2) { + long h1 = 0, h2 = 0, p1 = 1, p2 = 1; + String combined = s1 + "," + s2; + + for (char c : combined.toCharArray()) { + h1 = (h1 + (c - 96) * p1) % MOD1; + h2 = (h2 + (c - 96) * p2) % MOD2; + p1 = (p1 * BASE1) % MOD1; + p2 = (p2 * BASE2) % MOD2; + } + return (h1 << 32) | h2; + } + + public void checkIn(int id, String startStation, int t) { + checkInMap.put(id, new Pair<>(startStation, t)); + } + + public void checkOut(int id, String endStation, int t) { + Pair checkInData = checkInMap.get(id); + long routeHash = getHash(checkInData.getKey(), endStation); + routeMap.putIfAbsent(routeHash, new int[]{0, 0}); + int[] data = routeMap.get(routeHash); + data[0] += (t - checkInData.getValue()); + data[1]++; + } + + public double getAverageTime(String startStation, String endStation) { + long routeHash = getHash(startStation, endStation); + int[] data = routeMap.get(routeHash); + return (double) data[0] / data[1]; + } +} +``` + +```cpp +class UndergroundSystem { +private: + static constexpr int MOD1 = 768258391, MOD2 = 685683731; + static constexpr int BASE1 = 37, BASE2 = 31; + unordered_map> checkInMap; + unordered_map> routeMap; + + unsigned long long getHash(const string& s1, const string& s2) { + long long h1 = 0, h2 = 0, p1 = 1, p2 = 1; + string combined = s1 + "," + s2; + + for (char c : combined) { + h1 = (h1 + (c - 96) * p1) % MOD1; + h2 = (h2 + (c - 96) * p2) % MOD2; + p1 = (p1 * BASE1) % MOD1; + p2 = (p2 * BASE2) % MOD2; + } + return (h1 << 32) | h2; + } + +public: + UndergroundSystem() {} + + void checkIn(int id, string startStation, int t) { + checkInMap[id] = {startStation, t}; + } + + void checkOut(int id, string endStation, int t) { + auto [startStation, time] = checkInMap[id]; + unsigned long long routeHash = getHash(startStation, endStation); + routeMap[routeHash].first += (t - time); + routeMap[routeHash].second++; + } + + double getAverageTime(string startStation, string endStation) { + unsigned long long routeHash = getHash(startStation, endStation); + auto [totalTime, count] = routeMap[routeHash]; + return (double) totalTime / count; + } +}; +``` + +```javascript +class UndergroundSystem { + /** + * @constructor + */ + constructor() { + this.MOD1 = 768258391; + this.MOD2 = 685683731; + this.BASE1 = 37; + this.BASE2 = 31; + this.checkInMap = new Map(); + this.routeMap = new Map(); + } + + /** + * @param {string} s1 + * @param {string} s2 + * @return {number} + */ + getHash(s1, s2) { + let h1 = 0, h2 = 0, p1 = 1, p2 = 1; + let combined = s1 + "," + s2; + + for (let i = 0; i < combined.length; i++) { + let c = combined.charCodeAt(i) - 96; + h1 = (h1 + c * p1) % this.MOD1; + h2 = (h2 + c * p2) % this.MOD2; + p1 = (p1 * this.BASE1) % this.MOD1; + p2 = (p2 * this.BASE2) % this.MOD2; + } + return BigInt(h1) << BigInt(32) | BigInt(h2); + } + + /** + * @param {number} id + * @param {string} startStation + * @param {number} t + * @return {void} + */ + checkIn(id, startStation, t) { + this.checkInMap.set(id, [startStation, t]); + } + + /** + * @param {number} id + * @param {string} endStation + * @param {number} t + * @return {void} + */ + checkOut(id, endStation, t) { + let [startStation, time] = this.checkInMap.get(id); + let routeHash = this.getHash(startStation, endStation); + if (!this.routeMap.has(routeHash)) { + this.routeMap.set(routeHash, [0, 0]); + } + let data = this.routeMap.get(routeHash); + data[0] += t - time; + data[1]++; + } + + /** + * @param {string} startStation + * @param {string} endStation + * @return {number} + */ + getAverageTime(startStation, endStation) { + let routeHash = this.getHash(startStation, endStation); + let [totalTime, count] = this.routeMap.get(routeHash); + return totalTime / count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $checkIn()$ function call. + * $O(m)$ time for each $checkOut()$ and $getAverageTime()$ function calls. +* Space complexity: $O(n + N ^ 2)$ + +> Where $n$ is the number of passengers, $N$ is the total number of stations, and $m$ is the average length of station name. \ No newline at end of file diff --git a/articles/naming-a-company.md b/articles/naming-a-company.md new file mode 100644 index 000000000..dbd85775d --- /dev/null +++ b/articles/naming-a-company.md @@ -0,0 +1,495 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def distinctNames(self, ideas: List[str]) -> int: + n = len(ideas) + res = set() + ideasSet = set(ideas) + + for i in range(n): + for j in range(i + 1, n): + A, B = ideas[j][0] + ideas[i][1:], ideas[i][0] + ideas[j][1:] + if A not in ideasSet and B not in ideasSet: + res.add(A + ' ' + B) + res.add(B + ' ' + A) + + return len(res) +``` + +```java +public class Solution { + public long distinctNames(String[] ideas) { + int n = ideas.length; + Set res = new HashSet<>(); + Set ideasSet = new HashSet<>(Arrays.asList(ideas)); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + String A = ideas[j].charAt(0) + ideas[i].substring(1); + String B = ideas[i].charAt(0) + ideas[j].substring(1); + + if (!ideasSet.contains(A) && !ideasSet.contains(B)) { + res.add(A + " " + B); + res.add(B + " " + A); + } + } + } + + return res.size(); + } +} +``` + +```cpp +class Solution { +public: + long long distinctNames(vector& ideas) { + int n = ideas.size(); + unordered_set res; + unordered_set ideasSet(ideas.begin(), ideas.end()); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + string A = ideas[j][0] + ideas[i].substr(1); + string B = ideas[i][0] + ideas[j].substr(1); + + if (!ideasSet.count(A) && !ideasSet.count(B)) { + res.insert(A + " " + B); + res.insert(B + " " + A); + } + } + } + + return res.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} ideas + * @return {number} + */ + distinctNames(ideas) { + let n = ideas.length; + let res = new Set(); + let ideasSet = new Set(ideas); + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + let A = ideas[j][0] + ideas[i].slice(1); + let B = ideas[i][0] + ideas[j].slice(1); + + if (!ideasSet.has(A) && !ideasSet.has(B)) { + res.add(A + " " + B); + res.add(B + " " + A); + } + } + } + + return res.size; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n ^ 2)$ +* Space complexity: $O(m * n ^ 2)$ + +> Where $n$ is the size of the array $ideas$ and $m$ is the average length of the strings. + +--- + +## 2. Group By First Letter (Hash Map) + +::tabs-start + +```python +class Solution: + def distinctNames(self, ideas: List[str]) -> int: + wordMap = collections.defaultdict(set) + for w in ideas: + wordMap[w[0]].add(w[1:]) + + res = 0 + for char1 in wordMap: + for char2 in wordMap: + if char1 == char2: + continue + + intersect = sum(1 for w in wordMap[char1] if w in wordMap[char2]) + distinct1 = len(wordMap[char1]) - intersect + distinct2 = len(wordMap[char2]) - intersect + res += distinct1 * distinct2 + + return res +``` + +```java +public class Solution { + public long distinctNames(String[] ideas) { + Map> wordMap = new HashMap<>(); + for (String word : ideas) { + wordMap.computeIfAbsent( + word.charAt(0), k -> new HashSet<>()).add(word.substring(1) + ); + } + + long res = 0; + for (char char1 : wordMap.keySet()) { + for (char char2 : wordMap.keySet()) { + if (char1 == char2) continue; + + int intersect = 0; + for (String w : wordMap.get(char1)) { + if (wordMap.get(char2).contains(w)) { + intersect++; + } + } + + int distinct1 = wordMap.get(char1).size() - intersect; + int distinct2 = wordMap.get(char2).size() - intersect; + res += distinct1 * 1L * distinct2; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long distinctNames(vector& ideas) { + unordered_map> wordMap; + for (const string& word : ideas) { + wordMap[word[0]].insert(word.substr(1)); + } + + long long res = 0; + for (auto& [char1, set1] : wordMap) { + for (auto& [char2, set2] : wordMap) { + if (char1 == char2) continue; + + int intersect = 0; + for (const string& w : set1) { + if (set2.count(w)) { + intersect++; + } + } + + int distinct1 = set1.size() - intersect; + int distinct2 = set2.size() - intersect; + res += distinct1 * 1LL * distinct2; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} ideas + * @return {number} + */ + distinctNames(ideas) { + const wordMap = new Map(); + for (let word of ideas) { + let key = word[0]; + if (!wordMap.has(key)) { + wordMap.set(key, new Set()); + } + wordMap.get(key).add(word.slice(1)); + } + + let res = 0; + for (let [char1, set1] of wordMap) { + for (let [char2, set2] of wordMap) { + if (char1 === char2) continue; + + let intersect = 0; + for (let w of set1) { + if (set2.has(w)) { + intersect++; + } + } + + let distinct1 = set1.size - intersect; + let distinct2 = set2.size - intersect; + res += distinct1 * distinct2; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $n$ is the size of the array $ideas$ and $m$ is the average length of the strings. + +--- + +## 3. Group By First Letter (Array) + +::tabs-start + +```python +class Solution: + def distinctNames(self, ideas: List[str]) -> int: + suffixes = [set() for _ in range(26)] + for w in ideas: + suffixes[ord(w[0]) - ord('a')].add(w[1:]) + + res = 0 + for i in range(26): + for j in range(i + 1, 26): + intersect = len(suffixes[i] & suffixes[j]) + res += 2 * (len(suffixes[i]) - intersect) * (len(suffixes[j]) - intersect) + + return res +``` + +```java +public class Solution { + public long distinctNames(String[] ideas) { + Set[] suffixes = new HashSet[26]; + for (int i = 0; i < 26; i++) { + suffixes[i] = new HashSet<>(); + } + for (String w : ideas) { + suffixes[w.charAt(0) - 'a'].add(w.substring(1)); + } + + long res = 0; + for (int i = 0; i < 26; i++) { + for (int j = i + 1; j < 26; j++) { + int intersect = 0; + for (String s : suffixes[i]) { + if (suffixes[j].contains(s)) { + intersect++; + } + } + res += 2L * (suffixes[i].size() - intersect) * (suffixes[j].size() - intersect); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long distinctNames(vector& ideas) { + unordered_set suffixes[26]; + for (const string& w : ideas) { + suffixes[w[0] - 'a'].insert(w.substr(1)); + } + + long long res = 0; + for (int i = 0; i < 26; i++) { + for (int j = i + 1; j < 26; j++) { + int intersect = 0; + for (const string& s : suffixes[i]) { + if (suffixes[j].count(s)) { + intersect++; + } + } + res += 2LL * (suffixes[i].size() - intersect) * (suffixes[j].size() - intersect); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} ideas + * @return {number} + */ + distinctNames(ideas) { + const suffixes = Array.from({ length: 26 }, () => new Set()); + for (let w of ideas) { + suffixes[w.charCodeAt(0) - 97].add(w.slice(1)); + } + + let res = 0; + for (let i = 0; i < 26; i++) { + for (let j = i + 1; j < 26; j++) { + let intersect = 0; + for (let s of suffixes[i]) { + if (suffixes[j].has(s)) { + intersect++; + } + } + res += 2 * (suffixes[i].size - intersect) * (suffixes[j].size - intersect); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $n$ is the size of the array $ideas$ and $m$ is the average length of the strings. + +--- + +## 4. Counting + +::tabs-start + +```python +class Solution: + def distinctNames(self, ideas: List[str]) -> int: + mp = defaultdict(lambda: [False] * 26) + count = [[0] * 26 for _ in range(26)] + res = 0 + + for s in ideas: + first_char = ord(s[0]) - ord('a') + suffix = s[1:] + mp[suffix][first_char] = True + + for suffix, arr in mp.items(): + for i in range(26): + if arr[i]: + for j in range(26): + if not arr[j]: + count[i][j] += 1 + res += count[j][i] + + return 2 * res +``` + +```java +public class Solution { + public long distinctNames(String[] ideas) { + Map mp = new HashMap<>(); + int[][] count = new int[26][26]; + long res = 0; + + for (String s : ideas) { + int firstChar = s.charAt(0) - 'a'; + String suffix = s.substring(1); + mp.putIfAbsent(suffix, new boolean[26]); + mp.get(suffix)[firstChar] = true; + } + + for (boolean[] arr : mp.values()) { + for (int i = 0; i < 26; i++) { + if (arr[i]) { + for (int j = 0; j < 26; j++) { + if (!arr[j]) { + count[i][j]++; + res += count[j][i]; + } + } + } + } + } + return 2 * res; + } +} +``` + +```cpp +class Solution { +public: + long long distinctNames(vector& ideas) { + unordered_map> mp; + long long count[26][26] = {}; + long long res = 0; + + for (const string& s : ideas) { + int firstChar = s[0] - 'a'; + string suffix = s.substr(1); + mp[suffix][firstChar] = true; + } + + for (auto& [suffix, arr] : mp) { + for (int i = 0; i < 26; i++) { + if (arr[i]) { + for (int j = 0; j < 26; j++) { + if (!arr[j]) { + count[i][j]++; + res += count[j][i]; + } + } + } + } + } + return 2 * res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} ideas + * @return {number} + */ + distinctNames(ideas) { + const mp = new Map(); + const count = Array.from({ length: 26 }, () => Array(26).fill(0)); + let res = 0; + + for (const s of ideas) { + const firstChar = s.charCodeAt(0) - 97; + const suffix = s.slice(1); + if (!mp.has(suffix)) mp.set(suffix, new Array(26).fill(false)); + mp.get(suffix)[firstChar] = true; + } + + for (const arr of mp.values()) { + for (let i = 0; i < 26; i++) { + if (arr[i]) { + for (let j = 0; j < 26; j++) { + if (!arr[j]) { + count[i][j]++; + res += count[j][i]; + } + } + } + } + } + return 2 * res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $n$ is the size of the array $ideas$ and $m$ is the average length of the strings. \ No newline at end of file diff --git a/articles/number-of-submatrices-that-sum-to-target.md b/articles/number-of-submatrices-that-sum-to-target.md new file mode 100644 index 000000000..0fe84feca --- /dev/null +++ b/articles/number-of-submatrices-that-sum-to-target.md @@ -0,0 +1,549 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + + for r1 in range(ROWS): + for r2 in range(r1, ROWS): + for c1 in range(COLS): + for c2 in range(c1, COLS): + subSum = 0 + for r in range(r1, r2 + 1): + for c in range(c1, c2 + 1): + subSum += matrix[r][c] + + if subSum == target: + res += 1 + + return res +``` + +```java +public class Solution { + public int numSubmatrixSumTarget(int[][] matrix, int target) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int subSum = 0; + for (int r = r1; r <= r2; r++) { + for (int c = c1; c <= c2; c++) { + subSum += matrix[r][c]; + } + } + if (subSum == target) { + res++; + } + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubmatrixSumTarget(vector>& matrix, int target) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + int res = 0; + + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int subSum = 0; + for (int r = r1; r <= r2; r++) { + for (int c = c1; c <= c2; c++) { + subSum += matrix[r][c]; + } + } + if (subSum == target) { + res++; + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {number} + */ + numSubmatrixSumTarget(matrix, target) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0; + + for (let r1 = 0; r1 < ROWS; r1++) { + for (let r2 = r1; r2 < ROWS; r2++) { + for (let c1 = 0; c1 < COLS; c1++) { + for (let c2 = c1; c2 < COLS; c2++) { + let subSum = 0; + for (let r = r1; r <= r2; r++) { + for (let c = c1; c <= c2; c++) { + subSum += matrix[r][c]; + } + } + if (subSum === target) { + res++; + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ 3 * n ^ 3)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns of the given matrix. + +--- + +## 2. Two Dimensional Prefix Sum + +::tabs-start + +```python +class Solution: + def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + sub_sum = [[0] * COLS for _ in range(ROWS)] + + for r in range(ROWS): + for c in range(COLS): + top = sub_sum[r - 1][c] if r > 0 else 0 + left = sub_sum[r][c - 1] if c > 0 else 0 + top_left = sub_sum[r - 1][c - 1] if min(r, c) > 0 else 0 + sub_sum[r][c] = matrix[r][c] + top + left - top_left + + res = 0 + for r1 in range(ROWS): + for r2 in range(r1, ROWS): + for c1 in range(COLS): + for c2 in range(c1, COLS): + top = sub_sum[r1 - 1][c2] if r1 > 0 else 0 + left = sub_sum[r2][c1 - 1] if c1 > 0 else 0 + top_left = sub_sum[r1 - 1][c1 - 1] if min(r1, c1) > 0 else 0 + cur_sum = sub_sum[r2][c2] - top - left + top_left + if cur_sum == target: + res += 1 + return res +``` + +```java +public class Solution { + public int numSubmatrixSumTarget(int[][] matrix, int target) { + int ROWS = matrix.length, COLS = matrix[0].length; + int[][] subSum = new int[ROWS][COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int top = (r > 0) ? subSum[r - 1][c] : 0; + int left = (c > 0) ? subSum[r][c - 1] : 0; + int topLeft = (Math.min(r, c) > 0) ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + int res = 0; + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int top = (r1 > 0) ? subSum[r1 - 1][c2] : 0; + int left = (c1 > 0) ? subSum[r2][c1 - 1] : 0; + int topLeft = (Math.min(r1, c1) > 0) ? subSum[r1 - 1][c1 - 1] : 0; + int curSum = subSum[r2][c2] - top - left + topLeft; + if (curSum == target) { + res++; + } + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubmatrixSumTarget(vector>& matrix, int target) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + vector> subSum(ROWS, vector(COLS, 0)); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int top = (r > 0) ? subSum[r - 1][c] : 0; + int left = (c > 0) ? subSum[r][c - 1] : 0; + int topLeft = (min(r, c) > 0) ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + int res = 0; + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int top = (r1 > 0) ? subSum[r1 - 1][c2] : 0; + int left = (c1 > 0) ? subSum[r2][c1 - 1] : 0; + int topLeft = (min(r1, c1) > 0) ? subSum[r1 - 1][c1 - 1] : 0; + int curSum = subSum[r2][c2] - top - left + topLeft; + if (curSum == target) { + res++; + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {number} + */ + numSubmatrixSumTarget(matrix, target) { + const ROWS = matrix.length, COLS = matrix[0].length; + const subSum = Array.from({ length: ROWS }, () => Array(COLS).fill(0)); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + let top = r > 0 ? subSum[r - 1][c] : 0; + let left = c > 0 ? subSum[r][c - 1] : 0; + let topLeft = Math.min(r, c) > 0 ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + let res = 0; + for (let r1 = 0; r1 < ROWS; r1++) { + for (let r2 = r1; r2 < ROWS; r2++) { + for (let c1 = 0; c1 < COLS; c1++) { + for (let c2 = c1; c2 < COLS; c2++) { + let top = r1 > 0 ? subSum[r1 - 1][c2] : 0; + let left = c1 > 0 ? subSum[r2][c1 - 1] : 0; + let topLeft = Math.min(r1, c1) > 0 ? subSum[r1 - 1][c1 - 1] : 0; + let curSum = subSum[r2][c2] - top - left + topLeft; + if (curSum === target) { + res++; + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ 2 * n ^ 2)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns of the given matrix. + +--- + +## 3. Horizontal 1D Prefix Sum + +::tabs-start + +```python +class Solution: + def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + sub_sum = [[0] * COLS for _ in range(ROWS)] + + for r in range(ROWS): + for c in range(COLS): + top = sub_sum[r - 1][c] if r > 0 else 0 + left = sub_sum[r][c - 1] if c > 0 else 0 + top_left = sub_sum[r - 1][c - 1] if min(r, c) > 0 else 0 + sub_sum[r][c] = matrix[r][c] + top + left - top_left + + res = 0 + for r1 in range(ROWS): + for r2 in range(r1, ROWS): + count = defaultdict(int) + count[0] = 1 + for c in range(COLS): + cur_sum = sub_sum[r2][c] - (sub_sum[r1 - 1][c] if r1 > 0 else 0) + res += count[cur_sum - target] + count[cur_sum] += 1 + + return res +``` + +```java +public class Solution { + public int numSubmatrixSumTarget(int[][] matrix, int target) { + int ROWS = matrix.length, COLS = matrix[0].length; + int[][] subSum = new int[ROWS][COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int top = (r > 0) ? subSum[r - 1][c] : 0; + int left = (c > 0) ? subSum[r][c - 1] : 0; + int topLeft = (Math.min(r, c) > 0) ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + int res = 0; + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + Map count = new HashMap<>(); + count.put(0, 1); + for (int c = 0; c < COLS; c++) { + int curSum = subSum[r2][c] - (r1 > 0 ? subSum[r1 - 1][c] : 0); + res += count.getOrDefault(curSum - target, 0); + count.put(curSum, count.getOrDefault(curSum, 0) + 1); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubmatrixSumTarget(vector>& matrix, int target) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + vector> subSum(ROWS, vector(COLS, 0)); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int top = (r > 0) ? subSum[r - 1][c] : 0; + int left = (c > 0) ? subSum[r][c - 1] : 0; + int topLeft = (min(r, c) > 0) ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + int res = 0; + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + unordered_map count; + count[0] = 1; + for (int c = 0; c < COLS; c++) { + int curSum = subSum[r2][c] - (r1 > 0 ? subSum[r1 - 1][c] : 0); + res += count[curSum - target]; + count[curSum]++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {number} + */ + numSubmatrixSumTarget(matrix, target) { + let ROWS = matrix.length, COLS = matrix[0].length; + let subSum = Array.from({ length: ROWS }, () => Array(COLS).fill(0)); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + let top = r > 0 ? subSum[r - 1][c] : 0; + let left = c > 0 ? subSum[r][c - 1] : 0; + let topLeft = Math.min(r, c) > 0 ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + let res = 0; + for (let r1 = 0; r1 < ROWS; r1++) { + for (let r2 = r1; r2 < ROWS; r2++) { + let count = new Map(); + count.set(0, 1); + for (let c = 0; c < COLS; c++) { + let curSum = subSum[r2][c] - (r1 > 0 ? subSum[r1 - 1][c] : 0); + res += count.get(curSum - target) || 0; + count.set(curSum, (count.get(curSum) || 0) + 1); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ 2 * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns of the given matrix. + +--- + +## 4. Vertical 1D Prefix Sum + +::tabs-start + +```python +class Solution: + def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + + for c1 in range(COLS): + row_prefix = [0] * ROWS + for c2 in range(c1, COLS): + for r in range(ROWS): + row_prefix[r] += matrix[r][c2] + + count = defaultdict(int) + count[0] = 1 + cur_sum = 0 + for r in range(ROWS): + cur_sum += row_prefix[r] + res += count[cur_sum - target] + count[cur_sum] += 1 + + return res +``` + +```java +public class Solution { + public int numSubmatrixSumTarget(int[][] matrix, int target) { + int ROWS = matrix.length, COLS = matrix[0].length, res = 0; + + for (int c1 = 0; c1 < COLS; c1++) { + int[] rowPrefix = new int[ROWS]; + for (int c2 = c1; c2 < COLS; c2++) { + for (int r = 0; r < ROWS; r++) { + rowPrefix[r] += matrix[r][c2]; + } + + Map count = new HashMap<>(); + count.put(0, 1); + int curSum = 0; + + for (int r = 0; r < ROWS; r++) { + curSum += rowPrefix[r]; + res += count.getOrDefault(curSum - target, 0); + count.put(curSum, count.getOrDefault(curSum, 0) + 1); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubmatrixSumTarget(vector>& matrix, int target) { + int ROWS = matrix.size(), COLS = matrix[0].size(), res = 0; + + for (int c1 = 0; c1 < COLS; c1++) { + vector rowPrefix(ROWS, 0); + for (int c2 = c1; c2 < COLS; c2++) { + for (int r = 0; r < ROWS; r++) { + rowPrefix[r] += matrix[r][c2]; + } + + unordered_map count; + count[0] = 1; + int curSum = 0; + for (int r = 0; r < ROWS; r++) { + curSum += rowPrefix[r]; + res += count[curSum - target]; + count[curSum]++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {number} + */ + numSubmatrixSumTarget(matrix, target) { + let ROWS = matrix.length, COLS = matrix[0].length, res = 0; + + for (let c1 = 0; c1 < COLS; c1++) { + let rowPrefix = new Array(ROWS).fill(0); + for (let c2 = c1; c2 < COLS; c2++) { + for (let r = 0; r < ROWS; r++) { + rowPrefix[r] += matrix[r][c2]; + } + + let count = new Map(); + count.set(0, 1); + let curSum = 0; + + for (let r = 0; r < ROWS; r++) { + curSum += rowPrefix[r]; + res += count.get(curSum - target) || 0; + count.set(curSum, (count.get(curSum) || 0) + 1); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n ^ 2)$ +* Space complexity: $O(m)$ + +> Where $m$ is the number of rows and $n$ is the number of columns of the given matrix. \ No newline at end of file diff --git a/articles/text-justification.md b/articles/text-justification.md new file mode 100644 index 000000000..fb48dcabe --- /dev/null +++ b/articles/text-justification.md @@ -0,0 +1,181 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def fullJustify(self, words: List[str], maxWidth: int) -> List[str]: + res = [] + line, length = [], 0 + i = 0 + + while i < len(words): + if length + len(words[i]) + len(line) <= maxWidth: + line.append(words[i]) + length += len(words[i]) + i += 1 + else: + # Line complete + extra_space = maxWidth - length + remainder = extra_space % max(1, (len(line) - 1)) + space = extra_space // max(1, (len(line) - 1)) + for j in range(max(1, len(line) - 1)): + line[j] += " " * space + if remainder: + line[j] += " " + remainder -= 1 + res.append("".join(line)) + line, length = [], 0 + + # Handling last line + last_line = " ".join(line) + trail_space = maxWidth - len(last_line) + res.append(last_line + " " * trail_space) + return res +``` + +```java +public class Solution { + public List fullJustify(String[] words, int maxWidth) { + List res = new ArrayList<>(); + List line = new ArrayList<>(); + int length = 0, i = 0; + + while (i < words.length) { + // If the current word can fit in the line + if (length + words[i].length() + line.size() <= maxWidth) { + line.add(words[i]); + length += words[i].length(); + i++; + } else { + // Line complete + int extra_space = maxWidth - length; + int remainder = extra_space % Math.max(1, (line.size() - 1)); + int space = extra_space / Math.max(1, (line.size() - 1)); + + for (int j = 0; j < Math.max(1, line.size() - 1); j++) { + line.set(j, line.get(j) + " ".repeat(space)); + if (remainder > 0) { + line.set(j, line.get(j) + " "); + remainder--; + } + } + + res.add(String.join("", line)); + line.clear(); + length = 0; + } + } + + // Handling last line + String last_line = String.join(" ", line); + int trail_space = maxWidth - last_line.length(); + res.add(last_line + " ".repeat(trail_space)); + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector fullJustify(vector& words, int maxWidth) { + vector res; + vector line; + int length = 0, i = 0; + + while (i < words.size()) { + if (length + words[i].size() + line.size() <= maxWidth) { + line.push_back(words[i]); + length += words[i].size(); + i++; + } else { + // Line complete + int extra_space = maxWidth - length; + int remainder = extra_space % max(1, (int)(line.size() - 1)); + int space = extra_space / max(1, (int)(line.size() - 1)); + + for (int j = 0; j < max(1, (int)line.size() - 1); j++) { + line[j] += string(space, ' '); + if (remainder > 0) { + line[j] += " "; + remainder--; + } + } + + string justified_line = accumulate(line.begin(), line.end(), string()); + res.push_back(justified_line); + line.clear(); + length = 0; + } + } + + // Handling last line + string last_line = accumulate(line.begin(), line.end(), string(), + [](string a, string b) { + return a.empty() ? b : a + " " + b; + }); + int trail_space = maxWidth - last_line.size(); + last_line += string(trail_space, ' '); + res.push_back(last_line); + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {number} maxWidth + * @return {string[]} + */ + fullJustify(words, maxWidth) { + let res = []; + let line = [], length = 0, i = 0; + + while (i < words.length) { + if (length + words[i].length + line.length <= maxWidth) { + line.push(words[i]); + length += words[i].length; + i++; + } else { + // Line complete + let extra_space = maxWidth - length; + let remainder = extra_space % Math.max(1, line.length - 1); + let space = Math.floor(extra_space / Math.max(1, line.length - 1)); + + for (let j = 0; j < Math.max(1, line.length - 1); j++) { + line[j] += " ".repeat(space); + if (remainder > 0) { + line[j] += " "; + remainder--; + } + } + + res.push(line.join("")); + line = []; + length = 0; + } + } + + // Handling last line + let last_line = line.join(" "); + let trail_space = maxWidth - last_line.length; + res.push(last_line + " ".repeat(trail_space)); + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of words and $m$ is the average length of the words. \ No newline at end of file