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

Python如何判断一个生成器是另一个的前缀或连续子序列

未知长度生成器的连续子序列判断

我有两个长度未知的生成器A和B,需要判断B是否是A的连续子序列,最初实现代码如下:

def subseq(A, B):
    b0 = next(B)
    for a in A:
       if a == b0:
           break
    else: # 循环未触发break
        # A中找不到B的首元素,B一定不是A的子序列
        return False
    # 判断B剩余部分是否是A剩余部分的前缀
    return prefix(A, B)

def prefix(A, B):
    return all(a == b for a, b in zip(A, B))

上述实现中的prefix(A, B)函数存在逻辑缺陷:如果A剩余元素长度短于B剩余元素长度,会出现假阳性误判:
例如当A = 'abc'、B = 'abcd'(将二者视作生成器)时,return all(a == b for a, b in zip(A, B))会错误返回True。
但如果替换为zip_longest实现,又会出现反向的假阴性误判问题:
例如当A = 'abcd'、B = 'abc'时,return all(a == b for a, b in zip_longest(A, B))会错误返回False。


实现要求

需要实现按第二个参数的长度执行zip截断的逻辑,效果类似自定义函数zip_(A, B, ziplengthselect=1):

  • 参数ziplengthselect=i的作用是指定函数按第i个传入参数的长度执行zip截断。
    在该自定义zip函数的基础上,表达式all(a == b for a, b in zip_(A, B, fillvalue=sentinel, ziplengthselect=1))(其中sentinel是B中不会出现的哨兵值)可实现如下预期行为:
  • 遍历到B的末尾时,表达式返回True
  • 先遍历到A的末尾时,函数会填充哨兵值,此时判断sentinel == b会失败(因为哨兵值选取为B中不存在的值),返回False
  • 任意位置出现a == b判定不通过时,表达式返回False

已尝试的实现

目前已有基于try/except块的实现方案,希望了解是否存在更优雅的实现方式。
第一种prefix函数实现:

# 判断生成器B是否是生成器A的前缀
def prefix(A, B):
    for b in B:
        try:
            a = next(A)
            if a != b:
                return False
        except StopIteration:
            # 已遍历到A的末尾,A长度不足
            return False
    return True

第二种prefix函数实现:

# 判断生成器B是否是生成器A的前缀
def prefix(A, B):
    prefix_match = all(a == b for a, b in zip(A, B))
    if not prefix_match:
        return False
    try:
        next(B)
        # B仍有剩余元素,说明A长度不足
        return False
    except StopIteration:
        # B已遍历完成,匹配成功
        return True

支持重复元素的修正版本

上述代码在A无重复元素时可正常运行,但如果A存在重复元素,需要通过tee对生成器做拷贝处理,修正后的完整代码如下:

from itertools import tee

def subseq(A, B):
    try:
        b0 = next(B)
    except StopIteration:
        return True
    
    while True:
        try:
            a = next(A)
            if a == b0:
                A, Acop = tee(A)
                B, Bcop = tee(B)
                if prefix(Acop, Bcop):
                    return True
                del Acop, Bcop
        except StopIteration:
            return False
            
def prefix(A, B):
    for b in B:
        try:
            a = next(A)
            if a != b:
                return False
        except StopIteration:
            # 已遍历到A的末尾,长度不足
            return False
    return True


# 测试用例
A = (i for i in range(10))
B = (i for i in range(5,8))
print(subseq(A, B)) # 输出True

A = (i for i in range(10))
B = (i for i in range(5,11))
print(subseq(A, B)) # 输出False

A = (i for i in [1,2,3]*10 + [1,2,3,4])
B = (i for i in [1,2,3])
print(subseq(A, B)) # 输出True

A = (i for i in [1,1,2,1,1,2]*8 + [3])
B = (i for i in [1,1,2,3])
print(subseq(A, B)) # 输出True

参考:列表场景实现

列表场景下的同类连续子序列判断实现更简单,因为可以直接获取列表长度,对应代码如下:

def isSublist(lst, sublst):
    N, M = len(lst), len(sublst)
    starts = (i for i in range(N - M + 1) if lst[i] == sublst[0])

    for i in starts:
        # i <= N - M,因此N - i >= M,不会出现索引越界
        j = 0
        while j < M and lst[i] == sublst[j]:
            i += 1
            j += 1
        if j == M:
            return True
    return False

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 17:51:13