You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Trie在已分词句子列表中搜索短语并返回匹配的完整句子

基于Trie实现已分词句子的短语检索方案

核心思路

由于输入是已经完成分词的语料,我们可以直接构建token级Trie(而非字符级Trie),每个Trie节点对应一个分词结果,节点中存储所有包含当前节点对应前缀短语的完整句子,查询时沿Trie匹配目标短语的所有token,匹配完成后直接取节点中存储的句子即可。

具体实现步骤

1. 定义Trie节点结构

每个节点包含两个核心字段:

  • 子节点映射:key为分词token,value为对应子Trie节点
  • 句子集合:存储所有包含从根节点到当前节点路径对应短语的完整句子,可选存储句子ID减少内存占用

2. 构建Trie索引

遍历所有已分词的句子,对每个句子枚举所有可能的短语起始位置,将从该位置开始的所有连续token序列插入Trie,每插入一个token就将当前完整句子存入对应节点的句子集合中。

3. 执行短语查询

将目标短语先做分词(若输入已分词可跳过该步骤),从Trie根节点开始依次匹配每个token:

  • 任意token匹配失败直接返回空结果,说明无符合要求的句子
  • 所有token匹配完成后,返回当前节点存储的所有句子即可

可运行代码示例(Python)

from collections import defaultdict

class TrieNode:
    def __init__(self):
        # 子节点映射,key为分词token,value为子节点
        self.children = defaultdict(TrieNode)
        # 存储包含当前路径对应短语的句子,用set自动去重
        self.matched_sentences = set()

class PhraseSearchTrie:
    def __init__(self):
        self.root = TrieNode()
        # 可选:句子ID映射表,大语料下用ID代替原始句子存到节点中减少内存
        self.id_to_sent = dict()
        self.next_id = 0

    def add_sentence(self, tokenized_sent: list[str], raw_sent: str) -> None:
        """插入一条已分词的句子到Trie中"""
        # 大语料下可开启ID映射逻辑
        # sent_id = self.next_id
        # self.id_to_sent[sent_id] = raw_sent
        # self.next_id += 1

        sent_length = len(tokenized_sent)
        # 枚举所有短语起始位置
        for start_idx in range(sent_length):
            current_node = self.root
            # 插入从start_idx开始的所有连续token序列
            for end_idx in range(start_idx, sent_length):
                current_token = tokenized_sent[end_idx]
                current_node = current_node.children[current_token]
                # 存入当前句子,大语料下替换为sent_id
                current_node.matched_sentences.add(raw_sent)
    
    def search(self, tokenized_phrase: list[str]) -> list[str]:
        """查询包含目标短语的所有句子"""
        current_node = self.root
        for token in tokenized_phrase:
            if token not in current_node.children:
                return []
            current_node = current_node.children[token]
        # 大语料下这里替换为 [self.id_to_sent[id] for id in current_node.matched_sentences]
        return list(current_node.matched_sentences)

# 测试用例
if __name__ == "__main__":
    search_trie = PhraseSearchTrie()
    # 插入测试句子
    search_trie.add_sentence(["我", "喜欢", "吃", "苹果"], "我喜欢吃苹果")
    search_trie.add_sentence(["苹果", "富含", "维生素"], "苹果富含维生素")
    search_trie.add_sentence(["我", "明天", "买", "苹果"], "我明天买苹果")
    # 查询包含短语["吃", "苹果"]的句子
    print(search_trie.search(["吃", "苹果"])) # 输出: ['我喜欢吃苹果']
    # 查询包含短语["苹果"]的句子
    print(search_trie.search(["苹果"])) # 输出: ['我喜欢吃苹果', '苹果富含维生素', '我明天买苹果']

优化方案

  • 语料规模超过10万条时,建议用句子ID替代原始句子存入Trie节点,内存占用可降低70%以上
  • 若业务场景不需要支持超长短语查询,可限制插入Trie的短语最大长度(比如最多插入长度为15的短语),进一步降低内存消耗和构建耗时
  • 支持模糊匹配的场景可在节点中增加容错逻辑,常规精确匹配场景无需修改

内容的提问来源于stack exchange,提问作者Nnemeka300

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 07:39:00