diff --git a/solution/0600-0699/0676.Implement Magic Dictionary/README.md b/solution/0600-0699/0676.Implement Magic Dictionary/README.md index f7f70ca3b0acc..bdd8dbf2ccc66 100644 --- a/solution/0600-0699/0676.Implement Magic Dictionary/README.md +++ b/solution/0600-0699/0676.Implement Magic Dictionary/README.md @@ -430,80 +430,125 @@ class MagicDictionary { #### Rust ```rust -use std::collections::HashMap; - -#[derive(Clone)] struct Trie { children: Vec>>, - is_end: bool, + val: i32, } impl Trie { fn new() -> Self { Trie { - children: vec![None; 26], - is_end: false, + children: (0..26).map(|_| None).collect(), + val: 0, } } - fn insert(&mut self, word: &str) { + fn insert(&mut self, w: &str, x: i32) { let mut node = self; - for &ch in word.as_bytes() { - let index = (ch - b'a') as usize; - node = node.children[index].get_or_insert_with(|| Box::new(Trie::new())); + for c in w.chars() { + let idx = (c as usize) - ('a' as usize); + if node.children[idx].is_none() { + node.children[idx] = Some(Box::new(Trie::new())); + } + node = node.children[idx].as_mut().unwrap(); + node.val += x; } - node.is_end = true; } - fn search(&self, word: &str, diff: i32) -> bool { - if word.is_empty() { - return diff == 1 && self.is_end; + fn search(&self, w: &str) -> i32 { + let mut node = self; + for c in w.chars() { + let idx = (c as usize) - ('a' as usize); + if node.children[idx].is_none() { + return 0; + } + node = node.children[idx].as_ref().unwrap(); } + node.val + } +} - let index = (word.as_bytes()[0] - b'a') as usize; - if let Some(child) = &self.children[index] { - if child.search(&word[1..], diff) { - return true; - } +struct MapSum { + d: std::collections::HashMap, + trie: Trie, +} + +impl MapSum { + fn new() -> Self { + MapSum { + d: std::collections::HashMap::new(), + trie: Trie::new(), } + } - if diff == 0 { - for (i, child) in self.children.iter().enumerate() { - if i != index && child.is_some() { - if child.as_ref().unwrap().search(&word[1..], 1) { - return true; - } - } + fn insert(&mut self, key: String, val: i32) { + let x = val - self.d.get(&key).unwrap_or(&0); + self.d.insert(key.clone(), val); + self.trie.insert(&key, x); + } + + fn sum(&self, prefix: String) -> i32 { + self.trie.search(&prefix) + } +} +``` + +#### C# + +```cs +public class Trie { + private Trie[] children = new Trie[26]; + private int val; + + public void Insert(string w, int x) { + Trie node = this; + for (int i = 0; i < w.Length; ++i) { + int idx = w[i] - 'a'; + if (node.children[idx] == null) { + node.children[idx] = new Trie(); } + node = node.children[idx]; + node.val += x; } + } - false + public int Search(string w) { + Trie node = this; + for (int i = 0; i < w.Length; ++i) { + int idx = w[i] - 'a'; + if (node.children[idx] == null) { + return 0; + } + node = node.children[idx]; + } + return node.val; } } -struct MagicDictionary { - trie: Trie, -} +public class MapSum { + private Dictionary d = new Dictionary(); + private Trie trie = new Trie(); -/** - * `&self` means the method takes an immutable reference. - * If you need a mutable reference, change it to `&mut self` instead. - */ -impl MagicDictionary { - fn new() -> Self { - MagicDictionary { trie: Trie::new() } + public MapSum() { } - fn build_dict(&mut self, dictionary: Vec) { - for word in dictionary { - self.trie.insert(&word); - } + public void Insert(string key, int val) { + int x = val - (d.ContainsKey(key) ? d[key] : 0); + d[key] = val; + trie.Insert(key, x); } - fn search(&self, search_word: String) -> bool { - self.trie.search(&search_word, 0) + public int Sum(string prefix) { + return trie.Search(prefix); } } + +/** + * Your MapSum object will be instantiated and called as such: + * MapSum obj = new MapSum(); + * obj.Insert(key,val); + * int param_2 = obj.Sum(prefix); + */ ``` diff --git a/solution/0600-0699/0676.Implement Magic Dictionary/README_EN.md b/solution/0600-0699/0676.Implement Magic Dictionary/README_EN.md index 0eb29d8d584a3..152d13cea2e53 100644 --- a/solution/0600-0699/0676.Implement Magic Dictionary/README_EN.md +++ b/solution/0600-0699/0676.Implement Magic Dictionary/README_EN.md @@ -422,80 +422,125 @@ class MagicDictionary { #### Rust ```rust -use std::collections::HashMap; - -#[derive(Clone)] struct Trie { children: Vec>>, - is_end: bool, + val: i32, } impl Trie { fn new() -> Self { Trie { - children: vec![None; 26], - is_end: false, + children: (0..26).map(|_| None).collect(), + val: 0, } } - fn insert(&mut self, word: &str) { + fn insert(&mut self, w: &str, x: i32) { let mut node = self; - for &ch in word.as_bytes() { - let index = (ch - b'a') as usize; - node = node.children[index].get_or_insert_with(|| Box::new(Trie::new())); + for c in w.chars() { + let idx = (c as usize) - ('a' as usize); + if node.children[idx].is_none() { + node.children[idx] = Some(Box::new(Trie::new())); + } + node = node.children[idx].as_mut().unwrap(); + node.val += x; } - node.is_end = true; } - fn search(&self, word: &str, diff: i32) -> bool { - if word.is_empty() { - return diff == 1 && self.is_end; + fn search(&self, w: &str) -> i32 { + let mut node = self; + for c in w.chars() { + let idx = (c as usize) - ('a' as usize); + if node.children[idx].is_none() { + return 0; + } + node = node.children[idx].as_ref().unwrap(); } + node.val + } +} - let index = (word.as_bytes()[0] - b'a') as usize; - if let Some(child) = &self.children[index] { - if child.search(&word[1..], diff) { - return true; - } +struct MapSum { + d: std::collections::HashMap, + trie: Trie, +} + +impl MapSum { + fn new() -> Self { + MapSum { + d: std::collections::HashMap::new(), + trie: Trie::new(), } + } - if diff == 0 { - for (i, child) in self.children.iter().enumerate() { - if i != index && child.is_some() { - if child.as_ref().unwrap().search(&word[1..], 1) { - return true; - } - } + fn insert(&mut self, key: String, val: i32) { + let x = val - self.d.get(&key).unwrap_or(&0); + self.d.insert(key.clone(), val); + self.trie.insert(&key, x); + } + + fn sum(&self, prefix: String) -> i32 { + self.trie.search(&prefix) + } +} +``` + +#### C# + +```cs +public class Trie { + private Trie[] children = new Trie[26]; + private int val; + + public void Insert(string w, int x) { + Trie node = this; + for (int i = 0; i < w.Length; ++i) { + int idx = w[i] - 'a'; + if (node.children[idx] == null) { + node.children[idx] = new Trie(); } + node = node.children[idx]; + node.val += x; } + } - false + public int Search(string w) { + Trie node = this; + for (int i = 0; i < w.Length; ++i) { + int idx = w[i] - 'a'; + if (node.children[idx] == null) { + return 0; + } + node = node.children[idx]; + } + return node.val; } } -struct MagicDictionary { - trie: Trie, -} +public class MapSum { + private Dictionary d = new Dictionary(); + private Trie trie = new Trie(); -/** - * `&self` means the method takes an immutable reference. - * If you need a mutable reference, change it to `&mut self` instead. - */ -impl MagicDictionary { - fn new() -> Self { - MagicDictionary { trie: Trie::new() } + public MapSum() { } - fn build_dict(&mut self, dictionary: Vec) { - for word in dictionary { - self.trie.insert(&word); - } + public void Insert(string key, int val) { + int x = val - (d.ContainsKey(key) ? d[key] : 0); + d[key] = val; + trie.Insert(key, x); } - fn search(&self, search_word: String) -> bool { - self.trie.search(&search_word, 0) + public int Sum(string prefix) { + return trie.Search(prefix); } } + +/** + * Your MapSum object will be instantiated and called as such: + * MapSum obj = new MapSum(); + * obj.Insert(key,val); + * int param_2 = obj.Sum(prefix); + */ ``` diff --git a/solution/0600-0699/0676.Implement Magic Dictionary/Solution.cs b/solution/0600-0699/0676.Implement Magic Dictionary/Solution.cs new file mode 100644 index 0000000000000..e4361a2a35679 --- /dev/null +++ b/solution/0600-0699/0676.Implement Magic Dictionary/Solution.cs @@ -0,0 +1,53 @@ +public class Trie { + private Trie[] children = new Trie[26]; + private int val; + + public void Insert(string w, int x) { + Trie node = this; + for (int i = 0; i < w.Length; ++i) { + int idx = w[i] - 'a'; + if (node.children[idx] == null) { + node.children[idx] = new Trie(); + } + node = node.children[idx]; + node.val += x; + } + } + + public int Search(string w) { + Trie node = this; + for (int i = 0; i < w.Length; ++i) { + int idx = w[i] - 'a'; + if (node.children[idx] == null) { + return 0; + } + node = node.children[idx]; + } + return node.val; + } +} + +public class MapSum { + private Dictionary d = new Dictionary(); + private Trie trie = new Trie(); + + public MapSum() { + } + + public void Insert(string key, int val) { + int x = val - (d.ContainsKey(key) ? d[key] : 0); + d[key] = val; + trie.Insert(key, x); + } + + public int Sum(string prefix) { + return trie.Search(prefix); + } +} + +/** + * Your MapSum object will be instantiated and called as such: + * MapSum obj = new MapSum(); + * obj.Insert(key,val); + * int param_2 = obj.Sum(prefix); + */ diff --git a/solution/0600-0699/0676.Implement Magic Dictionary/Solution.rs b/solution/0600-0699/0676.Implement Magic Dictionary/Solution.rs index c8ce751b9f346..baac4b318e933 100644 --- a/solution/0600-0699/0676.Implement Magic Dictionary/Solution.rs +++ b/solution/0600-0699/0676.Implement Magic Dictionary/Solution.rs @@ -1,74 +1,61 @@ -use std::collections::HashMap; - -#[derive(Clone)] struct Trie { children: Vec>>, - is_end: bool, + val: i32, } impl Trie { fn new() -> Self { Trie { - children: vec![None; 26], - is_end: false, + children: (0..26).map(|_| None).collect(), + val: 0, } } - fn insert(&mut self, word: &str) { + fn insert(&mut self, w: &str, x: i32) { let mut node = self; - for &ch in word.as_bytes() { - let index = (ch - b'a') as usize; - node = node.children[index].get_or_insert_with(|| Box::new(Trie::new())); - } - node.is_end = true; - } - - fn search(&self, word: &str, diff: i32) -> bool { - if word.is_empty() { - return diff == 1 && self.is_end; - } - - let index = (word.as_bytes()[0] - b'a') as usize; - if let Some(child) = &self.children[index] { - if child.search(&word[1..], diff) { - return true; + for c in w.chars() { + let idx = (c as usize) - ('a' as usize); + if node.children[idx].is_none() { + node.children[idx] = Some(Box::new(Trie::new())); } + node = node.children[idx].as_mut().unwrap(); + node.val += x; } + } - if diff == 0 { - for (i, child) in self.children.iter().enumerate() { - if i != index && child.is_some() { - if child.as_ref().unwrap().search(&word[1..], 1) { - return true; - } - } + fn search(&self, w: &str) -> i32 { + let mut node = self; + for c in w.chars() { + let idx = (c as usize) - ('a' as usize); + if node.children[idx].is_none() { + return 0; } + node = node.children[idx].as_ref().unwrap(); } - - false + node.val } } -struct MagicDictionary { +struct MapSum { + d: std::collections::HashMap, trie: Trie, } -/** - * `&self` means the method takes an immutable reference. - * If you need a mutable reference, change it to `&mut self` instead. - */ -impl MagicDictionary { +impl MapSum { fn new() -> Self { - MagicDictionary { trie: Trie::new() } + MapSum { + d: std::collections::HashMap::new(), + trie: Trie::new(), + } } - fn build_dict(&mut self, dictionary: Vec) { - for word in dictionary { - self.trie.insert(&word); - } + fn insert(&mut self, key: String, val: i32) { + let x = val - self.d.get(&key).unwrap_or(&0); + self.d.insert(key.clone(), val); + self.trie.insert(&key, x); } - fn search(&self, search_word: String) -> bool { - self.trie.search(&search_word, 0) + fn sum(&self, prefix: String) -> i32 { + self.trie.search(&prefix) } } diff --git a/solution/0600-0699/0677.Map Sum Pairs/README.md b/solution/0600-0699/0677.Map Sum Pairs/README.md index 3d87dc6955cc0..b72f07706836c 100644 --- a/solution/0600-0699/0677.Map Sum Pairs/README.md +++ b/solution/0600-0699/0677.Map Sum Pairs/README.md @@ -379,6 +379,72 @@ class MapSum { */ ``` +#### JavaScript + +```js +class Trie { + constructor() { + this.children = new Array(26); + this.val = 0; + } + + insert(w, x) { + let node = this; + for (const c of w) { + const i = c.charCodeAt(0) - 97; + if (!node.children[i]) { + node.children[i] = new Trie(); + } + node = node.children[i]; + node.val += x; + } + } + + search(w) { + let node = this; + for (const c of w) { + const i = c.charCodeAt(0) - 97; + if (!node.children[i]) { + return 0; + } + node = node.children[i]; + } + return node.val; + } +} + +var MapSum = function () { + this.d = new Map(); + this.t = new Trie(); +}; + +/** + * @param {string} key + * @param {number} val + * @return {void} + */ +MapSum.prototype.insert = function (key, val) { + const x = val - (this.d.get(key) ?? 0); + this.d.set(key, val); + this.t.insert(key, x); +}; + +/** + * @param {string} prefix + * @return {number} + */ +MapSum.prototype.sum = function (prefix) { + return this.t.search(prefix); +}; + +/** + * Your MapSum object will be instantiated and called as such: + * var obj = new MapSum() + * obj.insert(key,val) + * var param_2 = obj.sum(prefix) + */ +``` + diff --git a/solution/0600-0699/0677.Map Sum Pairs/README_EN.md b/solution/0600-0699/0677.Map Sum Pairs/README_EN.md index 6ca47cee79f59..3c1b9c409295f 100644 --- a/solution/0600-0699/0677.Map Sum Pairs/README_EN.md +++ b/solution/0600-0699/0677.Map Sum Pairs/README_EN.md @@ -68,7 +68,20 @@ mapSum.sum("ap"); // return 5 (apple + app = 3 -### Solution 1 +### Solution 1: Hash Table + Trie + +We use a hash table $d$ to store key-value pairs and a trie $t$ to store the prefix sums of the key-value pairs. Each node in the trie contains two pieces of information: + +- `val`: the total sum of the values of the key-value pairs with this node as the prefix +- `children`: an array of length $26$ that stores the child nodes of this node + +When inserting a key-value pair $(key, val)$, we first check if the key exists in the hash table. If it does, the `val` of each node in the trie needs to subtract the original value of the key and then add the new value. If it does not exist, the `val` of each node in the trie needs to add the new value. + +When querying the prefix sum, we start from the root node of the trie and traverse the prefix string. If the current node's child nodes do not contain the character, it means the prefix does not exist in the trie, and we return $0$. Otherwise, we continue to traverse the next character until we finish traversing the prefix string and return the `val` of the current node. + +In terms of time complexity, the time complexity of inserting a key-value pair is $O(n)$, where $n$ is the length of the key. The time complexity of querying the prefix sum is $O(m)$, where $m$ is the length of the prefix. + +The space complexity is $O(n \times m \times C)$, where $n$ and $m$ are the number of keys and the maximum length of the keys, respectively; and $C$ is the size of the character set, which is $26$ in this problem. @@ -364,6 +377,72 @@ class MapSum { */ ``` +#### JavaScript + +```js +class Trie { + constructor() { + this.children = new Array(26); + this.val = 0; + } + + insert(w, x) { + let node = this; + for (const c of w) { + const i = c.charCodeAt(0) - 97; + if (!node.children[i]) { + node.children[i] = new Trie(); + } + node = node.children[i]; + node.val += x; + } + } + + search(w) { + let node = this; + for (const c of w) { + const i = c.charCodeAt(0) - 97; + if (!node.children[i]) { + return 0; + } + node = node.children[i]; + } + return node.val; + } +} + +var MapSum = function () { + this.d = new Map(); + this.t = new Trie(); +}; + +/** + * @param {string} key + * @param {number} val + * @return {void} + */ +MapSum.prototype.insert = function (key, val) { + const x = val - (this.d.get(key) ?? 0); + this.d.set(key, val); + this.t.insert(key, x); +}; + +/** + * @param {string} prefix + * @return {number} + */ +MapSum.prototype.sum = function (prefix) { + return this.t.search(prefix); +}; + +/** + * Your MapSum object will be instantiated and called as such: + * var obj = new MapSum() + * obj.insert(key,val) + * var param_2 = obj.sum(prefix) + */ +``` + diff --git a/solution/0600-0699/0677.Map Sum Pairs/Solution.js b/solution/0600-0699/0677.Map Sum Pairs/Solution.js new file mode 100644 index 0000000000000..2dee19e3d25c2 --- /dev/null +++ b/solution/0600-0699/0677.Map Sum Pairs/Solution.js @@ -0,0 +1,61 @@ +class Trie { + constructor() { + this.children = new Array(26); + this.val = 0; + } + + insert(w, x) { + let node = this; + for (const c of w) { + const i = c.charCodeAt(0) - 97; + if (!node.children[i]) { + node.children[i] = new Trie(); + } + node = node.children[i]; + node.val += x; + } + } + + search(w) { + let node = this; + for (const c of w) { + const i = c.charCodeAt(0) - 97; + if (!node.children[i]) { + return 0; + } + node = node.children[i]; + } + return node.val; + } +} + +var MapSum = function () { + this.d = new Map(); + this.t = new Trie(); +}; + +/** + * @param {string} key + * @param {number} val + * @return {void} + */ +MapSum.prototype.insert = function (key, val) { + const x = val - (this.d.get(key) ?? 0); + this.d.set(key, val); + this.t.insert(key, x); +}; + +/** + * @param {string} prefix + * @return {number} + */ +MapSum.prototype.sum = function (prefix) { + return this.t.search(prefix); +}; + +/** + * Your MapSum object will be instantiated and called as such: + * var obj = new MapSum() + * obj.insert(key,val) + * var param_2 = obj.sum(prefix) + */