【问题标题】:How could I make this sub-string finder more efficient and cleaner?我怎样才能使这个子字符串查找器更高效、更清洁?
【发布时间】:2021-09-25 13:17:07
【问题描述】:

这是我想到的第一个解决方案,但我真的无法想象另一个解决方案。

strings = [input("String: ") for i in range(int(input("How many strings?")))]
num = 0

for i, subA in enumerate(strings):
    for j,subB in enumerate(strings):
        if (i != j) and (subB in subA):
            num += 1
print("There are", num, "substrings")

【问题讨论】:

  • 你可以做类似len(strings.split(' '))
  • 如果你想看到性能提升,这里是你的选择:num = sum(starmap(contains, combinations(strings, 2)))。进口:contains()combinations()starmap()。你可以用它来代替你的嵌套循环。
  • 对不起,上面的例子有错误,你应该使用permutations()而不是combinations()num = sum(starmap(contains, permutations(strings, 2)))
  • @OlvinR​​oght,非常感谢,现在我只需要了解它的实际工作原理

标签: python for-loop iteration


【解决方案1】:

在您的实现中,您正在重新发明itertools.permutations(),它返回具有给定长度的元素的排列。您可以比较使用嵌套的for 循环(来自您的代码示例)permutations() 生成的列表:

from itertools import permutations

strings = ["a", "aa", "b"]
res = []
for i, subA in enumerate(strings):
    for j, subB in enumerate(strings):
        if i != j:
            res.append((subA, subB))
print("Nested loop:", res)
res = list(permutations(strings, 2))
print("permutations():", res)

您需要检查每个元素是否是另一个元素的子字符串,因此您可以迭代从permutations() 返回的对,并测试第一个元素是否包含第二个(反之亦然)。让我们用简单的列表理解来做吧:

from itertools import permutations

strings = ["a", "aa", "b"]
res = [a in b for a, b in permutations(strings, 2)]
# will return [True, False, False, False, False, False]

在 python 中,True1False0 (docs)。因此,要计算有多少字符串是子字符串,我们可以将 生成器表达式 传递给 sum()

from itertools import permutations

strings = ["a", "aa", "b"]
num = sum(a in b for a, b in permutations(strings, 2))
# will return 1

您也可以使用itertools.starmap() 调用operator.contains() (与a in b 相同) 每对都由permutations() 返回。

from operator import contains
from itertools import permutations, starmap

strings = ["a", "aa", "b"]
num = sum(starmap(contains, permutations(strings, 2)))

这是您的代码的一些改进版本:

from operator import contains
from itertools import permutations, starmap

count = input("How many strings? ")
if count.isdecimal() and (count := int(count)):
    strings = []
    while count:
        item = input(f"String ({count} left): ")
        if item:  # skip empty strings
            strings.append(item)
            count -= 1
    num = sum(starmap(contains, permutations(strings, 2)))
    print("There", "are" if num > 1 else "is", num or "no",
          "substring" + "s" * (num != 1))
else:
    print(f'"{count}" is not a valid positive number')

P.S.关于性能的一些说明。

由于sum() 处理可迭代的方法,您可以使用生成器表达式对代码进行一些补丁以更快地工作。

sum([1 for a, b in permutations(strings, 2) if a in b])

会略快于

sum(a in b for a, b in permutations(strings, 2))

为什么?看看接下来的问题:

  1. Why is summing list comprehension faster than generator expression?;
  2. Why is any (True for ... if cond) much faster than any (cond for ...)?

【讨论】:

    【解决方案2】:
    strings = [input("String: ") for i in range(int(input("How many strings?")))]
    num = 0
    
    for i, subA in enumerate(strings[:-1]):
        for subB in strings[i+1:]:
            if subB in subA or subA in subB:
                num += 1
    print("There are", num, "substrings")
    

    现在,我不确定我是否理解您要执行的操作,但这可以确保每个字符串只与所有其他字符串进行一次比较。它未经测试,所以不要相信我的话。

    我假设,给定一个字符串数组,您想找出这些字符串中有多少包含任何其他字符串作为子字符串。请注意,如果是这种情况,则存在如何处理列表中相同字符串的问题。

    【讨论】:

    • 思路是检查字符串“b”是否包含在字符串“a”中,并且不能是同一个字符串,所以代码要检查数组中的每个字符串每个字符串,这就是为什么我要让它更快
    • 相同的字符串将被计为子字符串,并且使用 i 和 j 索引我们会阻止相同的项目对其自身进行计数
    • 是的,我通过仅将每个字符串与列表中的后续字符串进行比较以及将这些字符串与相关字符串进行比较来解决这个问题。你的代码的复杂度是 O(n^2) 而我的应该是 O(nlogn)
    • 只有当字符串列表包含相同的字符串时,我们算法的输出才会有所不同。假设你的列表中有 ["a", "b", "c", "abaa", "aca", "u", "caa", "abracadabra", "a", "b"]代码第一个“a”将与最后一个“a”进行比较,反之亦然,从而触发计数的两个加法,在我的算法中,它只会被添加一次,因为两者同时进行比较。如果您喜欢它,您可以将其更改为 num += 1 +(subA 中的 subB 和 subB 中的 subA)。
    • 所以我得到了不同的答案,而额外的添加实际上是答案的一部分,因为最后一个“a”是第一个“a”的一部分/等于。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-16
    • 1970-01-01
    • 1970-01-01
    • 2013-08-29
    • 1970-01-01
    • 2010-11-14
    • 1970-01-01
    相关资源
    最近更新 更多