使用正则表达式忽略字符的子字符串

时间:2013-09-23 15:00:48

标签: python regex string

我的问题如下,我想找到字符串中所有出现的子字符串。更具体地说,我想查找所有索引集s[0]...s[n],以便stst[s[0]], st[s[1], ... st[s[n]]字符串匹配搜索到的子字符串。 “但为什么??”你问,好吧......因为它在这里on code jam。通过对所有可能的排列进行顺序比较很容易解决,但是对于大字符串来说它会变得很慢。所以我想到了正则表达式。

作为一个例子,对于字符串'abcoeubc'和子串'abc',它应该给我索引[(0,1,2),(0,1,7),(0,6,7)]或类似的东西。我真的不想要指数,只是计算子发生次数。我一直在尝试像

这样的东西
import re
r = re.compile(r'a.*b.*c')
matches = [m for i in r.finditer('abcoeubc')]

但它并没有像我预期的那样表现。我也用前瞻性的表达方式尝试过,用r = re.compile(r'(?=a).*(?=b).*(?=c)')之类的东西,但是也不行。我尝试使用正则表达式是错误的吗?

2 个答案:

答案 0 :(得分:3)

修改

子编辑:添加了一些多汁的低调水果。稍微晦涩,明显更快。

你想要速度吗?

超级速度?

嗯...

from bisect import bisect_right

def count_ascending_permutations(sequence_indexes, i, max=float("inf")):
    last = sequence_indexes[i]
    end = bisect_right(last, max)

    return sum(
        count_ascending_permutations(sequence_indexes, i-1, item)
        for item in last[:end]
    ) if i else end

def count_allpaths(target, sequence):
    sequence_chars = {k: [] for k in sequence}
    for i, character in enumerate(target):
        if character in sequence_chars:
            sequence_chars[character].append(i)

    sequence_indexes = [sequence_chars[character] for character in sequence]

    return count_ascending_permutations(sequence_indexes, len(sequence_indexes)-1)

<小时/> <小时/>

无法使用正则表达式执行此操作,因为正则表达式不会找到所有可能的匹配项,只显示位置与您正在搜索的正则表达式匹配。

这是一个解决方案,我将更新解释:

from itertools import takewhile

def ascending_permutations(sequence_indexes, i, max=float("inf")):
    last = takewhile(lambda item: item < max, sequence_indexes[i])

    if i == 0:
        for item in last:
            yield [item]

    for item in last:
        for subitems in ascending_permutations(sequence_indexes, i-1, item):
            subitems.append(item)
            yield subitems

def allpaths(target, sequence):
    sequence_indexes = []
    for character in sequence:
        sequence_indexes.append([i for i, c in enumerate(target) if c == character])

    return ascending_permutations(sequence_indexes, len(sequence_indexes)-1)

list(allpaths("abcoeubcbc", "abc"))
#>>> [[0, 1, 2], [0, 1, 7], [0, 6, 7], [0, 1, 9], [0, 6, 9], [0, 8, 9]]

足够的编辑,解释!

如果您有字符abcoeubcbc,并且您希望按顺序排列字符abc,那么您正在查看

0123456780
abcoeubcbc
----------
abc       
ab     c  
a     bc  
ab       c
a       bc

不是向前阅读,这是明显的解释,而是向后读

查看每个c的位置。它依次位于第2,7和0位。

当它处于位置0时,你可以忽略它之后的所有内容,因为那些都是无序的:

012
abc
---
abc

b做同样的事情。好吧,它只有一个位置,a也一样,所以这很容易。到下一部分:

01234567
abcoeubc
--------
ab     c
a     bc

b可以分为两个地方。在这两种情况下,a都有一个广告位。

然后是最终位置:

0123456780
abcoeubcbc
----------
ab       c
a       bc

再次b有两个职位,然后我们递归每个职位,a只有一个职位。

在我解决了我的饥饿之后,更多关于这与下面的代码的关系!


我回来了,比我预期的要晚一点。我想,不要匆忙,因为OP似乎甚至没有注意到我......

首先我们应该看一下allpaths

def allpaths(target, sequence):
    sequence_indexes = []
    for character in sequence:
        sequence_indexes.append([i for i, c in enumerate(target) if c == character])

    return ascending_permutations(sequence_indexes, len(sequence_indexes)-1)

allpaths包装函数 - 除了为ascending_permutations设置条件之外,它实际上并没有多少实现。这是必需的,因为正如您稍后将看到的,ascending_permutations是递归的,我们只想运行此部分一次。

首先,

for character in sequence:
    sequence_indexes.append([i for i, c in enumerate(target) if c == character])

为每个字符生成单词中每个出现的索引。这是一个“矩阵”,因为它是一个列表列表:

  abcoeubcbc
  ----------
a|0         |  →  [[0,     ],
b| 1    6 8 |  →   [1, 6, 8],
c|  2    7 9|  →   [2, 7, 9]]
  ----------

当前方法需要O(len(target) × len(sequence)),可以使用字典将其优化为O(len(target) + len(sequence))

sequence_chars = {k: [] for k in sequence}
for i, character in enumerate(target):
    if character in sequence_chars:
        sequence_chars[character].append(i)

sequence_indexes = [sequence_chars[character] for character in sequence]

这很酷,可以解决。

然后它将此矩阵发送给ascending_permutations,它完成实际工作。

ascending_permutations从列表末尾开始向后工作。这可能听起来很奇怪,但它是一个有根据的结构。

假设您有阶乘的递归算法:

def fact(n):
    if n == 1:
        return n

    return n * fact(n-1)

调用fact(3)执行fib(3) == 3 * fib(2) == 3 * (2 * fib(1)) == 3 * (2 * (1))我们可以看到,由于括号,我们在乘法时从1 → 2 → 3向外工作。因为我们希望使用append来构建我们的列表(快速到append,慢到insert(0, item))我们想要这样做:

(((our_list).append(a's position)).append(b's position)).append(c's position)

因此我们可以看到最外面的范围是c,而不是a。因此,我们应该从a开始。

我们也将len(sequence_indexes)-1传递给ascending_permutations,因为我们不想继续pop项目并将其推回去;我们将进行递归递归,并且更容易跟踪我们认为“结束”的位置。 len(sequence_indexes)-1sequence_indexes中最后一项的位置,在这种情况下,c的索引很多。

所以,现在谈谈这个职能部门......


def ascending_permutations(sequence_indexes, i, max=float("inf")):

max跟踪不同类型的结尾;当i跟踪字母时,max会跟踪要搜索到的最高索引:

     max→

     abcoeubcbc
     ----------
i  a|0         |  →  [[0,     ],
↓  b| 1    6 8 |  →   [1, 6, 8],
   c|  2    7 9|  →   [2, 7, 9]]
     ----------

然后我们想要遍历我们的“活动”字母,这是最后一封信:

    last = takewhile(lambda item: item < max, sequence_indexes[i])

请注意,我们使用takewhile将数字裁剪为max,因此没有索引超过我们之前计算过的字母。 max从无限开始,所以在你选择一封信之前没有限制!

然后我们有一个结束条件:

    if i == 0:
        for item in last:
            yield [item]

基本上这表示如果你只有一个字母,那么你的“路径”就是字母的索引。

最后,我们处于递归的核心。

对于我们的信件所在的每个索引,我们需要单独递归。对于c,我们会递归到索引279,例如。

    for item in last:

然后我们需要获得“裁剪”金额的所有路径。

        for subitems in ascending_permutations(sequence_indexes, i-1, item):

记住这一点:

01234567
abcoeubc
--------
ab     c
a     bc

?这就是递归的作用:它给出了c的特定位置的子集(在本例中为7),并将问题简化为该部分。

现在我们有了该子部分的列表,我们可以将我们的位置(在本例中为7,记住)添加到结尾并将结果抛给“上游”。

            subitems.append(item)
            yield subitems

就是这样。简单,嗯?

答案 1 :(得分:0)

所以这里是我如何在没有正则表达式的情况下解决它,但是对于大输入来说它变得非常慢。我会尝试检查替代实施......

import re
def simple_match( string, pattern ):
    count = 0
    # if there's only one character left, return the number of occurrences
    if len(pattern)==1:
        return len(re.findall( pattern, string ) )
    # otherwise find all occurrences of the first char of the remaining string
    pos = [i.start() for i in re.finditer( pattern[0], string )]
    # and for each position
    for i in pos:
        # check if the next char still comes up in the remaining string
        next2 = re.search( pattern[1], string[i:])
        # if it doesn't it, there are no valid substrings remaining, so break
        if next2 is None:
            break
        # else recur with the remaining string and pattern
        count+=simple_match( string[(i+1):], pattern[1:] )
    return count

可能效率较低,但不要真正理解为什么。