存在2+ gram时移除对应1-gram并还原文本顺序的问题求助
实现方案
核心逻辑是先给所有n-gram绑定它在原文本中的位置区间,先标记所有被长度≥2的gram覆盖的单词位置,再过滤掉被覆盖的1-gram,最后按位置拼接即可,全程不会打乱原文本顺序。
- 第一步:预处理原文本位置映射
先把输入的原文本按空格切分成单词列表,给每个单词分配从0开始的连续整数索引,方便后续给每个gram计算覆盖范围。
以你给出的输入为例,切分后索引映射为:0:I, 1:love, 2:ice, 3:cream, 4:and, 5:the, 6:second, 7:person - 第二步:给所有输出的gram计算位置覆盖区间
遍历你当前函数输出的所有(gram文本, 标签)元组,对每个gram按空格切分成单词,匹配它在原单词列表里的连续起始、结束索引,记录成(gram文本, 标签, 起始索引, 结束索引)的结构。
比如ice cream切分后是['ice','cream'],对应原列表索引2到3,覆盖区间就是[2,3];the second person对应索引5到7,覆盖区间是[5,7]。 - 第三步:标记所有被长gram覆盖的单词位置
新建一个空集合存被覆盖的索引,遍历所有gram,只要gram的单词长度≥2,就把它覆盖区间内的所有索引都加入这个集合。
你的示例里最终被覆盖的索引集合是{2,3,5,6,7}。 - 第四步:过滤无效1-gram
再次遍历所有带位置信息的gram,保留规则:- 单词长度≥2的gram全部保留
- 1-gram只有当它的索引不在「被覆盖索引集合」里时才保留
按这个规则过滤你的示例数据,剩下的条目为:(I, None, 0,0), (love, 'B2',1,1), (ice cream, 'A1',2,3), (and, 'A1',4,4), (the second person, 'B2',5,7)
- 第五步:按位置排序拼接文本
把过滤后的所有条目按「起始索引」从小到大排序,依次取出每个条目的gram文本,用空格拼接,得到的结果就和原输入文本完全一致。
参考代码片段
def filter_ngrams(original_text: str, raw_ngram_list: list[tuple[str, str|None]]) -> str: # 1. 构建原文本单词列表 words = original_text.split() word_len = len(words) # 2. 给每个gram匹配位置信息 ngram_with_pos = [] for gram_text, tag in raw_ngram_list: gram_words = gram_text.split() gram_seg_len = len(gram_words) # 滑动匹配连续子序列位置,避免重复单词匹配错位 start_idx = None for i in range(word_len - gram_seg_len + 1): if words[i:i+gram_seg_len] == gram_words: start_idx = i break if start_idx is None: raise ValueError("gram不在原文本中") end_idx = start_idx + gram_seg_len - 1 ngram_with_pos.append( (gram_text, tag, start_idx, end_idx, gram_seg_len) ) # 3. 收集所有长度≥2的gram覆盖的单词索引 covered_idx = set() for item in ngram_with_pos: _, _, s, e, seg_len = item if seg_len >=2: covered_idx.update(range(s, e+1)) # 4. 过滤被覆盖的1-gram filtered = [] for item in ngram_with_pos: gram_text, _, s, _, seg_len = item if seg_len >=2: filtered.append(item) else: if s not in covered_idx: filtered.append(item) # 5. 按原位置排序后拼接 filtered.sort(key=lambda x:x[2]) return " ".join([item[0] for item in filtered]) # 测试你的示例数据 raw_list = [('I', None), ('love', 'B2'), ('ice', 'A2'), ('ice cream', 'A1'), ('cream', 'A2'), ('and', 'A1'), ('the', 'A1'), ('the second person', 'B2'), ('second', 'B1'), ('person', 'A1')] origin = "I love ice cream and the second person" print(filter_ngrams(origin, raw_list)) # 输出结果和原输入完全一致:I love ice cream and the second person
要是原文本里有重复出现的单词,别直接拿单词值查索引,就用上面代码里的滑动窗口做连续子序列匹配,不会出现位置匹配错误的问题。
内容的提问来源于stack exchange,提问作者Alan Kashkash
相关产品推荐
相关产品推荐

