1. 问题描述:
实现一个 MapSum 类,支持两个方法,insert 和 sum:
MapSum() 初始化 MapSum 对象
void insert(String key, int val) 插入 key-val 键值对,字符串表示键 key ,整数表示值 val 。如果键 key 已经存在,那么原来的键值对将被替代成新的键值对。
int sum(string prefix) 返回所有以该前缀 prefix 开头的键 key 的值的总和。
示例:
输入:
["MapSum", "insert", "sum", "insert", "sum"]
[[], ["apple", 3], ["ap"], ["app", 2], ["ap"]]
输出:
[null, null, 3, null, 5]
解释:
MapSum mapSum = new MapSum();
mapSum.insert("apple", 3);
mapSum.sum("ap"); // return 3 (apple = 3)
mapSum.insert("app", 2);
mapSum.sum("ap"); // return 5 (apple + app = 3 + 2 = 5)
提示:
1 <= key.length, prefix.length <= 50
key 和 prefix 仅由小写英文字母组成
1 <= val <= 1000
最多调用 50 次 insert 和 sum
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/map-sum-pairs
2. 思路分析:
分析题目可以知道这道题目与力扣的676题是类似的,我们需要设计一个数据结构使得能够存储所有的单词,并且能够查询以当前前缀的前缀和。因为涉及到字符串的存储与字符串前缀的查询操作,所以我们想到使用Trie树来解决(Trie树特别适合这种字符串存储 + 前缀查询的问题),因为使用的是python语言所以可以使用字典来存储链接字符与字符之间的关系(比较习惯使用字典来维护整棵Trie树),但是由于这道题目需要维护当前单词的权重和所有单词的前缀和,所以下面使用Trie树的另外一个写法,这个写法会比较方便存储前缀和信息,使用数组的方式来链接字符与字符之间的关系,因为字符串的长度最多是50,并且最多调用insert函数50次所以节点个数最多为2500,我们可以声明长一点的二维数组son[2510][26],第一维用来唯一标识当前是哪个字符串的前缀,在插入单词的过程使用一个全局变量idx来维护,每插入一个新的节点那么idx加1,这样通过这个idx就可以唯一标识每一个单词,第二维度表示当前前缀对应的最后一个字母,son[i][j]表示当前的字符串前缀中的最后一个字母为j。因为这道题目需要维护两个信息,所以我们需要声明两个数组v,s分别记录当前某个单词的权重,某个字符串前缀的总和,我们在插入单词的时候维护这两个数组的值即可,使用idx的好处是可以唯一标识当前的字符串前缀所以我们在插入单词的时候维护对应位置的v,s的值即可。对于v的值我们可以在最后循环结束之后将对应位置赋值为val即可,对于s我们则需要在插入每一个字符的时候维护对应的前缀和。
3. 代码如下:
class MapSum:
N = 2510
son, v, s, idx = None, None, None, 1
# Trie树的相关操作, 与之前Trie树不同的是这里需要维护单词的前缀和信息, 所以需要使用数组来链接字符与字符之间的关系这样会比较方便
def __init__(self):
# 初始化
N = self.N
self.son = [[0] * 26 for i in range(N)]
# v存储当前单词的权重
self.v = [0] * N
# s存储当前前缀的前缀和
self.s = [0] * N
self.idx = 1
# last变量来记录Trie树中当前单词的权重, 不存在当前的单词则为0, 使用last变量可以在计算的时候防止重复插入前缀和, 因为last存在的时候val - last会抵消为0
def add(self, key: str, val: int, last: int) -> None:
p = 0
son = self.son
v = self.v
s = self.s
for c in key:
u = ord(c) - ord("a")
if son[p][u] == 0:
son[p][u] = self.idx
self.idx += 1
p = son[p][u]
s[p] += val - last
v[p] = val
def insert(self, key: str, val: int) -> None:
self.add(key, val, self.v[self.query(key)])
def query(self, prefix: str):
p = 0
son = self.son
for c in prefix:
u = ord(c) - ord("a")
if son[p][u] == 0: return 0
p = son[p][u]
return p
# 查询对应前缀和数组的下标即可
def sum(self, prefix: str) -> int:
s = self.s
return s[self.query(prefix)]