【问题标题】:String regex two mismatches Python字符串正则表达式两个不匹配的Python
【发布时间】:2015-09-30 15:26:05
【问题描述】:

如何扩展下面的代码以允许我探索子字符串和父字符串之间有 2 个或更少不匹配的所有实例?

子字符串:SSQP

字符串匹配到:SSPQQQQPSSSSQQQSSQPSPSQSSQPSSQPPSSSSQPSPSQSSQPSSSSQPSPSQSSQPSSSSQPSPSQ

这是一个示例,其中仅包含一种可能的不匹配:

>>> s = 'SSPQQQQPSSSSQQQSSQPSPSQSSQPSSQPPSSSSQPSPSQSSQPSSSSQPSPSQSSQPSSSSQPSPSQ'
>>> re.findall(r'(?=(SSQP|[A-Z]SQP|S[A-Z]QP|SS[A-Z]P|SSQ[A-Z]))', s)
['SSQQ', 'SSQP', 'SSQP', 'SSQP', 'SSQP', 'SSQP', 'SSQP', 'SSQP', 'SSQP']

显然,在上面的代码中合并两个不匹配的可能性将需要对所有可能的组合进行大量暴力输入。

如何扩展此代码(或重构此代码)以探索两个不匹配的可能性?

此外,我想修改我的输出,以便获得子字符串与字符串匹配的确切位置返回的数字索引(不是SSQQSSQP)。

【问题讨论】:

    标签: python regex string-matching


    【解决方案1】:

    组合爆炸对于四分之二的错配来说并没有那么糟糕。

    首先,请注意您可以省略 SSQP 本身,因为它包含在所有更宽松的情况下。

    re.findall(r'(?=([A-Z]SQP|S[A-Z]QP|SS[A-Z]P|SSQ[A-Z]))', s)
    

    所以,病例数是

                   4!
    C(4, 1) = ––––––––––––– = 4
              1! * (4 - 1)!
    

    对于最多两个不匹配,案例数为

                   4!
    C(4, 2) = ––––––––––––– = 6
              2! * (4 - 2)!
    

    即,

    re.findall('(?=(SS..|S.Q.|S..P|.SQ.|.S.P|..QP))', s)
    

    (为了简化说明,我冒昧写了. 而不是[A-Z]。)


    获取匹配的位置而不是匹配的文本:

    [match.start(0) for match in re.finditer('(?=(SS..|S.Q.|S..P|.SQ.|.S.P|..QP))', s)]
    

    【讨论】:

    • 我明白你在做什么,但我有 10 个有时 20 个字母,有时更多的子字符串,我很难使用 re 模块进行扩展。也许除了正则表达式之外我还可以使用其他东西?我喜欢你的“组合爆炸”的措辞,我会把这个术语放在我的武器库中:-)
    • 那你为什么问 C(4,2) 而不是你真正想要的?你说的 20 个错误中有多少个?
    • 长度为 20 的子字符串中可能出现 0、1 或 2 个错误。组合地写出这样的东西是一种皇家痛苦。在 OP 中,我只是想提供一个 MWE。
    【解决方案2】:

    你不必在这里使用re,你可以使用itertools模块代替,节省大量内存。

    您可以先提取所有长度为 4 的子字符串,然后将它们与您的 substring 进行比较,然后选择与您的 substring 相差小于 2 的子字符串:

    from itertools import izip,islice,tee
    
    def sub_findre(s,substring,diffnumber):
        sublen=len(substring)
        zip_gen=(izip(substring,islice(s,i,i+sublen)) for i in xrange(len(s)))
        for z in zip_gen:
            l,z=tee(z)
            if sum(1 for i,j in l if i==j)>=sublen-diffnumber:
                new=izip(*z)
                next(new)
                yield ''.join(next(new))
    

    演示:

    s='SSPQQQQPSSSSQQQSSQPSPSQSSQPSSQPPSSSSQPSPSQSSQPSSSSQPSPSQSSQPSSSSQPSPSQ'
    
    substring='SSQP'
    print list(sub_findre(s,substring,2))
    
    ['SSPQ', 'SPQQ', 'QQQP', 'SSSS', 'SSSQ', 'SSQQ', 'SQQQ', 'SSQP', 'PSQS', 'SSQP', 'SSQP', 'SQPP', 'SSSS', 'SSSQ', 'SSQP', 'PSQS', 'SSQP', 'SSSS', 'SSSQ', 'SSQP', 'PSQS', 'SSQP', 'SSSS', 'SSSQ', 'SSQP', 'PSQ']
    

    如果要返回索引,则需要将索引放入izip,您可以使用itertools.repeat() 重复索引长度为substring

    from itertools import izip,islice,tee,repeat
    
    def sub_findre(s,substring,diffnumber):
        sublen=len(substring)
        zip_gen=(izip(substring,islice(s,i,i+sublen),repeat(i,sublen)) for i in xrange(len(s)))
        for z in zip_gen:
            l,z=tee(z)
            if sum(1 for i,j,_ in l if i==j)>=sublen-diffnumber:
                new=izip(*z)
                next(new)
                next(new)
                yield next(new)[0]
    

    演示:

    print list(sub_findre(s,substring,2))
    [0, 1, 4, 8, 9, 10, 11, 15, 20, 23, 27, 28, 32, 33, 34, 39, 42, 46, 47, 48, 53, 56, 60, 61, 62, 67]
    

    【讨论】:

    • 确实,正则表达式完全是错误的工具。对于 20 个错误中的 2 个错误,模式中将有 190 个替代项。
    • 能否返回索引号,类似于match.start(0) 200_success 的技术?
    • @Kasra 接受!你介意我在接下来的几天里上传一个我目前正在开发的算法来解决这个问题吗?我想我可能有一个替代解决方案,它使用的技术与您在这里使用的非常有趣的技术不同。
    • @warship mm 好的,我会尝试 ;) 但我仍然认为可以优化答案,使用生成器在内存使用方面是非常有效的方式,虽然它比正则表达式快得多,我认为使用像字典这样的数据结构会更好,因为它使用哈希表,你可以从中获得更快的速度。
    • @Kasra 酷!如果我也发现一些有趣的东西或替代方法,我会将我的解决方案作为答案发布,您可以对其进行评估并让我知道您的想法:-)
    猜你喜欢
    • 2020-11-29
    • 2011-11-28
    • 1970-01-01
    • 1970-01-01
    • 2018-07-16
    • 2012-09-26
    • 1970-01-01
    • 2020-09-19
    • 1970-01-01
    相关资源
    最近更新 更多