Sponsored
Sponsored
Using HashMap
This approach uses two hash maps. The first map stores the keys and their respective values, while the second map, usually called the prefix map, is used to cache the sums of prefixes. Every time a key-value pair is inserted, we iteratively update the sums of all prefixes for that key. In this approach, every insert
operation might take time proportional to the length of the key, and the sum
operation is almost constant time thanks to the prefix sums map.
Time Complexity: O(k)
for insert, where k
is the length of the key. O(1)
for sum.
Space Complexity: O(n)
, where n
is the number of keys stored.
1class MapSum {
2 constructor() {
3 this.map = new Map();
4 this.prefixMap = new Map();
5 }
6
7 insert(key, val) {
8 const delta = val - (this.map.get(key) || 0);
9 this.map.set(key, val);
10
11 for (let i = 1; i <= key.length; i++) {
12 const prefix = key.slice(0, i);
13 this.prefixMap.set(prefix, (this.prefixMap.get(prefix) || 0) + delta);
14 }
15 }
16
17 sum(prefix) {
18 return this.prefixMap.get(prefix) || 0;
19 }
20}
The JavaScript solution uses the Map object to store key-value pairs and prefix sums. The insert
method adjusts each prefix's sum by the delta value, while the sum
method simply retrieves the value from the prefix map.
Using Trie
This approach involves using a Trie (Prefix Tree) for storing keys. Along with each node of the Trie, maintain a map that stores prefix sum. Every time a key is inserted, traverse each node and update the corresponding prefix sum. When retrieving a sum for a given prefix, traverse the Trie up to the end of the prefix and return the stored prefix sum.
Time Complexity: O(k)
for insert and O(p)
for sum, where k
is the length of the key and p
is the length of the prefix.
Space Complexity: O(n)
, where n
is the number of keys stored in the Trie.
1#include <string>
class TrieNode {
public:
std::unordered_map<char, TrieNode*> children;
int value = 0;
};
class MapSum {
TrieNode* root;
std::unordered_map<std::string, int> map;
public:
MapSum() {
root = new TrieNode();
}
void insert(std::string key, int val) {
int delta = val - map[key];
map[key] = val;
TrieNode* node = root;
for (char c : key) {
if (!node->children[c]) {
node->children[c] = new TrieNode();
}
node = node->children[c];
node->value += delta;
}
}
int sum(std::string prefix) {
TrieNode* node = root;
for (char c : prefix) {
if (!node->children[c]) return 0;
node = node->children[c];
}
return node->value;
}
};
The C++ implementation builds a Trie using the TrieNode class, where each node holds a map to its children and a value for prefix sum. The insert updates or adds new nodes in the Trie, updating values as it goes, while the sum function follows the nodes corresponding to the entered prefix.