defbuild(): """初始化Trie,根节点编号为1""" global cnt cnt = 1
definsert(word: str): """将字符串插入Trie""" global cnt cur = 1 pas[cur] += 1 for char in word: path = get_path(char) # 将字符映射到路径索引 if tree[cur][path] == 0: cnt += 1 tree[cur][path] = cnt cur = tree[cur][path] pas[cur] += 1 end[cur] = word # 或设置为True
defsearch(word: str) -> bool: """搜索字符串是否存在""" cur = 1 for char in word: path = get_path(char) if tree[cur][path] == 0: returnFalse cur = tree[cur][path] return end[cur] isnotNone
defcount_prefix(prefix: str) -> int: """计算以prefix为前缀的字符串数量""" cur = 1 for char in prefix: path = get_path(char) if tree[cur][path] == 0: return0 cur = tree[cur][path] return pas[cur]
definsert(word: str): """将差值字符串插入Trie""" global cnt cur = 1 pas[cur] += 1 for char in word: path = get_path(char) if tree[cur][path] == 0: cnt += 1 tree[cur][path] = cnt cur = tree[cur][path] pas[cur] += 1
defcount(pre: str) -> int: """计算以 pre 为前缀的字符串数量""" cur = 1 for char in pre: path = get_path(char) if tree[cur][path] == 0: return0 cur = tree[cur][path] return pas[cur]
defcount_consistent_keys(b, a): """主函数,处理接头密匙逻辑""" build() # 将所有 a 的差值序列插入Trie for nums in a: diff_str_parts = [] for i inrange(1, len(nums)): diff_str_parts.append(str(nums[i] - nums[i-1])) diff_str_parts.append("#") insert("".join(diff_str_parts)) ans = [0] * len(b) # 查询每个 b 的差值序列在前缀树中的计数 for i inrange(len(b)): nums = b[i] iflen(nums) <= 1: # 如果 b 只有一个或零个元素,差值序列为空 # 空前缀匹配所有插入的 a,所以结果是 a 的总数 ans[i] = len(a) else: diff_str_parts = [] for j inrange(1, len(nums)): diff_str_parts.append(str(nums[j] - nums[j-1])) diff_str_parts.append("#") ans[i] = count("".join(diff_str_parts)) return ans
MAXN = 3000001 tree = [[0] * 2for _ inrange(MAXN)] cnt = 0 high = 0
defbuild(nums): """构建Trie树,插入所有数字的二进制表示""" global cnt, high cnt = 1 # 找到数组中的最大值,以确定需要处理的二进制位数 maximum = max(nums) if nums else0 # high: 最高位的索引。例如,max=13(1101), bit_length=4, high=3 high = maximum.bit_length() - 1if maximum > 0else -1#bit_length返回的是二进制所需的位数,maximum>0表示没有有效位数 for num in nums: insert(num)
definsert(num): """将一个数字的二进制位插入Trie""" global cnt cur = 1 # 从最高有效位开始 for i inrange(high, -1, -1): path = (num >> i) & 1#将 num 的二进制表示向右移动 i 位,结果只保留最低位(第0位) if tree[cur][path] == 0: cnt += 1 tree[cur][path] = cnt cur = tree[cur][path]
defmax_xor(num): #就前缀树的这个 """对于给定的num,在Trie中寻找能使其异或结果最大的数""" ans = 0 cur = 1 for i inrange(high, -1, -1): # status: num 在第 i 位的状态 (0 or 1) status = (num >> i) & 1 # want: 希望遇到的对方路径,即与 status相反的位,这样异或结果在该位上是1 want = 1 - status # 检查Trie中是否存在期望的路径 if tree[cur][want] == 0: # 如果期望的路径不存在,只能走另一条路 want = 1 - want # (status ^ want) 是当前位实际的异或结果 # 将其左移 i 位,加到 ans 中 ans |= (status ^ want) << i #计算当前位的异或结果,将结果左移到正确的位置,累加到最终答案中 cur = tree[cur][want] #在Trie树中移动到下一个节点 return ans
defclear(): """清空Trie""" for i inrange(1, cnt + 1): tree[i][0] = tree[i][1] = 0
deffindMaximumXOR1(nums): """ Trie解法主函数 """ ifnot nums orlen(nums) < 2: return0 build(nums) ans = 0 for num in nums: ans = max(ans, max_xor(num)) clear() return ans
方法二:哈希表解法
核心思想
使用前缀树存储所有数字的二进制表示,然后贪心地寻找最大异或值:
假设我们已经确定了最大值的前k位(从高到低),结果是 ans。
现在我们来确定第 i 位。我们贪心地希望第 i 位是 1。 令我们的目标 better = ans | (1 << i)。
问题转化为:是否存在两个数 p 和 q,使得 (p XOR q) 的前缀等于 better? 这等价于 p 的前缀等于 better 的前缀 XORq 的前缀。
deffindMaximumXOR2(nums): """ 哈希表解法主函数 核心思想: 从最高位向最低位,一位一位地确定最大异或值的可能值。 """ ifnot nums orlen(nums) < 2: return0 maximum = max(nums) high_bit = maximum.bit_length() - 1if maximum > 0else -1 ans = 0 seen_prefixes = set() for i inrange(high_bit, -1, -1): # 贪心目标:尝试让第 i 位为 1 better = ans | (1 << i) seen_prefixes.clear() # 先clear掉hashset里的所有元素 # 将所有数字的 i-位前缀放入集合 for num in nums: seen_prefixes.add(num >> i) # 检查 better 这个前缀是否可以由集合中的某两个数异或得到 # a ^ b = c <=> a ^ c = b found = False for p in seen_prefixes: #遍历hashset里的所有元素 if (better >> i) ^ p in seen_prefixes: ans = better found = True break # if any(((better >> i) ^ p) in seen_prefixes for p in seen_prefixes): # ans = better
return ans
算法分析
时间复杂度:O(n * logV),V是数值范围
空间复杂度:O(n * logV)
核心技巧:二进制前缀树 + 贪心搜索
题目三:在二维字符数组中搜索可能的单词
问题描述
给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words,返回所有二维网格上的单词。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中”相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。
# 全局变量模拟静态数组 MAXN = 30001# words.length * words[i].length tree = [[0] * 26for _ inrange(MAXN)] pas = [0] * MAXN end = [None] * MAXN cnt = 0
defbuild(words: List[str]): """将所有待查单词构建成一棵Trie树""" global cnt cnt = 1 for word in words: cur = 1 pas[cur] += 1 for char in word: path = ord(char) - ord('a') if tree[cur][path] == 0: cnt += 1 tree[cur][path] = cnt # 把当前节点 cur 在这条路径 path 上的“指针”设为新节点编号 cur = tree[cur][path] # 在Trie树中移动到下一个节点 pas[cur] += 1 end[cur] = word
defclear(): """清理Trie树""" for i inrange(1, cnt + 1): for j inrange(26): tree[i][j] = 0 pas[i] = 0 end[i] = None
deffindWords(board: List[List[str]], words: List[str]) -> List[str]: """ 主函数 """ build(words) ans = [] for i inrange(len(board)): for j inrange(len(board[0])): dfs(board, i, j, 1, ans) # 根节点编号为1 clear() return ans