实现一个 MapSum 类里的两个方法,insert
和 sum
。
对于方法 insert
,你将得到一对(字符串,整数)的键值对。字符串表示键,整数表示值。如果键已经存在,那么原来的键值对将被替代成新的键值对。
对于方法 sum
,你将得到一个表示前缀的字符串,你需要返回所有以该前缀开头的键的值的总和。
示例 1:
输入: insert("apple", 3), 输出: Null
输入: sum("ap"), 输出: 3
输入: insert("app", 2), 输出: Null
输入: sum("ap"), 输出: 5
思路一:构建一个词典前缀树,构建的方法参见Implement Trie (Prefix Tree) 实现 Trie (前缀树),那么我们只需要修改startsWith方法即可,原题中只需要返回是否存在prefix的字符串,而这道题要求返回以prefix前缀的string的对应int的累加和。所以我们在遍历到字符串为prefix的前缀树时需要一个变量res来记录累加的和,这里注意前缀字符串prefix必须全部满足才算找到一个,比如prefix为“abc”,那么当匹配到“ab”时,即使是一个单词,也不能算在内。并且当匹配完“abc”后,我们需要BFS或者DFS来遍历剩下的每一种情况,由于前缀树是类似决策树的分支结构,所以不存在重复性扫描的问题,所以不需要记忆数组visited来保存已经访问过的节点。
参考代码如下:
class MapSum {
struct Trie {
bool isWord;
Trie* children[26] = {};
Trie() :isWord(false) {
for (auto &child : children) child = nullptr;
}
};
class TrieNode {
public:
TrieNode() {
root = new Trie();
}
void insert(string &word) {
Trie *p = root;
for (auto &ch : word) {
if (!p->children[ch - 'a']) p->children[ch - 'a'] = new Trie();
p = p->children[ch - 'a'];
}
p->isWord = true;
}
void packageInsert(string word, int val) {
insert(word);
m[word] = val;
}
void dfs(Trie* p,int &sum, string tmp) {
if (p->isWord) {
sum += m[tmp];
}
for (int i = 0; i < 26; i++) {
char ch = ('a' + i);
if (p->children[i]) dfs(p->children[i], sum, tmp + ch);
}
}
int searchPrefix(string &pre) {
int res=0;
Trie *p = root;
string tmp;
for (auto &ch : pre) {
if (!p->children[ch - 'a']) return res;
p = p->children[ch - 'a'];
}
tmp = pre;
dfs(p, res, tmp);
return res;
}
private:
Trie * root;
unordered_map<string, int> m;
};
public:
/** Initialize your data structure here. */
MapSum() {
node = new TrieNode();
}
void insert(string key, int val) {
node->packageInsert(key, val);
}
int sum(string prefix) {
return node->searchPrefix(prefix);
}
private:
TrieNode * node;
};
思路二:需要两个hashmap,m和score,其中m保存每一个输入的<string,int>的对应关系,而score直接保存对应prefix的分数,具体做法为每当有insert操作时,我们计算一个delta值=val-m[string],然后对string的每一个char都更新这个score,即如下核心代码:
void insert(string key, int val) {
int delta = val - m[key];
m[key] = val;
string prefix = "";
for (auto &ch : key) {
prefix += ch;
score[prefix] = ((score.find(prefix)==score.end())?0:score[prefix] + delta);
}
}
相当于我们保存每一个string的所有第一个字符开始的子序列的分数,并且这个操作的巧妙之处在于可以完成:覆盖和替换的功能,那么当调用sum函数时,我们直接返回score[prefix]即可。
参考代码如下:
class MapSum {
public:
/** Initialize your data structure here. */
MapSum() {
}
void insert(string key, int val) {
int delta = val - m[key];
m[key] = val;
string prefix = "";
for (auto &ch : key) {
prefix += ch;
score[prefix] = ((score.find(prefix)==score.end())?0:score[prefix] + delta);
}
}
int sum(string prefix) {
return score[prefix];
}
private:
unordered_map<string, int> m;
unordered_map<string, int> score;
};
/**
* Your MapSum object will be instantiated and called as such:
* MapSum obj = new MapSum();
* obj.insert(key,val);
* int param_2 = obj.sum(prefix);
*/