Map Sum Pairs — Solution in Python, Java & C++
Map Sum Pairs solution explained: step-by-step approach, complexity and code in Python, Java, C++ and JavaScript. A medium problem on strings and hash table.
Map Sum Pairs is a medium coding problem on strings, hash table and trie, asked in Amazon and Google interviews.
- Difficulty: Medium
- Topics: Strings, Hash Table, Trie
- Asked at: Amazon, Google
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
Design a map from string keys to integer values that can also add up the values of all keys sharing a prefix. Write the class MapSum:
MapSum()creates an empty map.void insert(String key, int value)stores the pair(key, value). Ifkeyis already present, its old value is replaced.int sum(String prefix)returns the sum of the values of every key that starts withprefix.
The judge calls the methods in the order listed in the first input line, with the arguments in the second; the expected output holds each call's result, null for the constructor and for every void method.
Example 1
Input: ["MapSum","insert","sum","insert","sum"]
[[],["apple",3],["ap"],["app",2],["ap"]]
Output: [null,null,3,null,5]
Explanation: Only "apple" starts with "ap" at first (3). After inserting "app" with 2, both keys start with "ap": 3 + 2 = 5.
Example 2
Input: ["MapSum","insert","insert","sum","insert","sum","sum"]
[[],["cat",5],["car",4],["ca"],["cat",1],["ca"],["dog"]]
Output: [null,null,null,9,null,5,0]
Explanation: "cat" and "car" give 5 + 4 = 9. Re-inserting "cat" with 1 replaces 5, so the sum drops to 1 + 4 = 5. No key starts with "dog".
Constraints
1 <= key.length, prefix.length <= 50key and prefix consist of only lowercase English letters1 <= value <= 1000At most 50 calls are made to insert and sum
Hints for Map Sum Pairs
Hint 1
Summing over every stored key on each query works, but a trie lets the prefix itself lead you to exactly the keys that share it.
Hint 2
Store at every trie node the total value of all keys passing through it; then
sum(prefix)is the total at the prefix's node.Hint 3
Re-inserting a key must replace its value: remember each key's current value and add only the difference
value - oldalong its path.
How to solve Map Sum Pairs
A trie whose every node carries the sum of the values of all keys passing through it. A query walks the prefix and reads one number; an insert walks its key and adds the change in that key's value to every node on the way.
Approach
- Each node holds children,
total(sum over keys through it) andown(the value of the key ending exactly here, 0 if none). insert(key, value): walkkey, creating missing nodes, to its last node; letdelta = value - own, setown = value.- Walk
keyagain from the root and adddeltatototalof every node on the path. sum(prefix): walkprefix; if a child is missing return 0, otherwise return the last node'stotal.
Why it works
A key's value contributes to the total of exactly the nodes on its path, which are the nodes of its prefixes. Adding delta keeps every such total equal to the sum of the current values, because the old value was counted there and is replaced by the new one. The node of prefix therefore holds the sum over all keys starting with prefix.
Complexity
- Time —
O(L) per insert and per sum, L = length of the string - Space —
O(total letters inserted)
Pitfalls
- Adding
valueinstead ofvalue - olddouble-counts a key that is inserted twice. - A prefix with no node in the trie sums to 0, not to an error.
Reference solution
Python
class MapSum:
def __init__(self):
# letter -> child; "#" = total of the keys through this node, "=" = value of the key ending here
self.root = {"#": 0}
def insert(self, key: str, value: int) -> None:
path = [self.root]
for ch in key:
path.append(path[-1].setdefault(ch, {"#": 0}))
delta = value - path[-1].get("=", 0)
path[-1]["="] = value
for node in path:
node["#"] += delta
def sum(self, prefix: str) -> int:
node = self.root
for ch in prefix:
if ch not in node:
return 0
node = node[ch]
return node["#"]Java
class MapSum {
private static class Node {
Node[] next = new Node[26];
int total, own;
}
private Node root = new Node();
public MapSum() {
}
public void insert(String key, int value) {
Node node = root;
for (int i = 0; i < key.length(); i++) {
int c = key.charAt(i) - 'a';
if (node.next[c] == null) node.next[c] = new Node();
node = node.next[c];
}
int delta = value - node.own;
node.own = value;
node = root;
root.total += delta;
for (int i = 0; i < key.length(); i++) {
node = node.next[key.charAt(i) - 'a'];
node.total += delta;
}
}
public int sum(String prefix) {
Node node = root;
for (int i = 0; i < prefix.length() && node != null; i++) node = node.next[prefix.charAt(i) - 'a'];
return node == null ? 0 : node.total;
}
}C++
class MapSum {
struct Node {
Node* next[26] = {};
int total = 0, own = 0;
};
Node* root = new Node();
public:
MapSum() {}
void insert(string key, int value) {
vector<Node*> path{root};
for (char ch : key) {
Node*& child = path.back()->next[ch - 'a'];
if (!child) child = new Node();
path.push_back(child);
}
int delta = value - path.back()->own;
path.back()->own = value;
for (Node* node : path) node->total += delta;
}
int sum(string prefix) {
Node* node = root;
for (size_t i = 0; i < prefix.size() && node; i++) node = node->next[prefix[i] - 'a'];
return node ? node->total : 0;
}
};JavaScript
var MapSum = function() {
this.root = { children: {}, total: 0, own: 0 };
};
MapSum.prototype.insert = function(key, value) {
var path = [this.root], node = this.root;
for (var i = 0; i < key.length; i++) {
var ch = key[i];
if (!node.children[ch]) node.children[ch] = { children: {}, total: 0, own: 0 };
node = node.children[ch];
path.push(node);
}
var delta = value - node.own;
node.own = value;
for (var k = 0; k < path.length; k++) path[k].total += delta;
};
MapSum.prototype.sum = function(prefix) {
var node = this.root;
for (var i = 0; i < prefix.length; i++) {
node = node.children[prefix[i]];
if (!node) return 0;
}
return node.total;
};Also on the editorial tab: C, C#, Go, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.
All 515 strings problems · the whole catalogue
Learn the technique: Strings · Hashing: Hash Maps and Hash Sets