【问题标题】:How can I check if a string has the same characters? Python如何检查字符串是否具有相同的字符? Python
【发布时间】:2013-08-21 22:15:36
【问题描述】:

我需要能够辨别任意长度的字符串,大于 1(并且只有小写字母),在基本字符串或模板字符串中是否具有相同的字符集。

以字符串“aabc”为例:“azbc”和“aaabc”为假,而“acba”为真。

在 python 中是否有一种快速的方法可以做到这一点,而无需跟踪第一个字符串的所有排列,然后将其与测试字符串进行比较?

【问题讨论】:

  • 重复有没有关系? aaaaaaabc 将如何比较?
  • 那是错误的,我会编辑我的问题。
  • 如果您针对同一个键测试大量值,则创建所有排列可能会更快。不过这会消耗大量额外的内存。
  • @MonteCarlo:当重复很重要时,谈论“集合”有点误导。
  • @gnibbler:将排列存储在压缩树或哈希树中而不是哈希表中可能是一个有趣的权衡。

标签: python string python-2.7


【解决方案1】:

对两个字符串排序,然后比较:

sorted(str1) == sorted(str2)

如果字符串的长度可能不同,您可能需要先确定这一点以节省时间:

len(str1) == len(str2) and sorted(str1) == sorted(str2)

【讨论】:

  • 您添加的优化可能有用,但其他很多东西也可能有用(例如,str1 == str2 or sorted(str1) == sorted(str2));除非您知道这是一个瓶颈并且您对数据有所了解,否则我不会费心增加复杂性。
  • @abarnert:我确实注意到只有当两者的长度不同时才会如此。如果字符串很长,即使只有少数不相等,也可能会节省时间。
  • 非常感谢,我是新来的,肯定有一种比进行所有排列以及检查模板或测试字符串的每个排列更直观的方法。
  • @DavidRobinson:当然,这可能会节省时间,但如果节省的时间无关紧要,那就不值得增加复杂性。
【解决方案2】:

这是O(n) 解决方案

from collections import Counter
Counter(str1) == Counter(str2)

但是对于n 的合理值,使用sortedO(n * log n) 解决方案可能更快

【讨论】:

  • 我不知道为什么,但根据我的 timeit 测试,这个解决方案实际上似乎很慢。也许开销太大?我在回答中提供了测试结果。
  • @Joowani,是的,Counter 的工作速度相当慢 - 但是出现错误的机会更少,所以这是一个权衡。也许有一天有人会更好地优化它
【解决方案3】:

这是另一个 O(n) 解决方案,比其他解决方案更长但稍快:

def cmp(str1, str2):
    if len(str1) != len(str2):
        return False

    d, d2 = {}, {}
    for char in str1:
        if char not in d:
            d[char] = 1
        else:
            d[char] += 1
    for char in str2:
        if char not in d:
            return False
        if char not in d2:
            d2[char] = 1
        else:
            d2[char] += 1

    return d == d2

它基本上与 gnibber 的解决方案做同样的事情(但出于一些奇怪的原因,集合库中的 Counter() 似乎很慢)。以下是一些timeit结果:

setup = '''
import collections
from collections import Counter

s1 = "abcdefghijklmnopqrstuvwxyz" * 10000
s2 = s1[::-1]

def cmp1(str1, str2):
    if len(str1) != len(str2):
        return False

    d, d2 = {}, {}
    for char in str1:
        if char not in d:
            d[char] = 1
        else:
            d[char] += 1
    for char in str2:
        if char not in d:
            return False
        if char not in d2:
            d2[char] = 1
        else:
            d2[char] += 1
    return d == d2

def cmp2(str1, str2):
    return len(str1) == len(str2) and sorted(str1) == sorted(str2)

def cmp3(str1, str2):    
    return Counter(str1) == Counter(str2)

def cmp4(str1, str2):
    if len(str1) != len(str2):
        return False
    d = collections.defaultdict(int)
    for c in str1:
        d[c] += 1
    for c in str2:
        d[c] -= 1
    return all(v == 0 for v in d.itervalues())
'''

    timeit.timeit("cmp1(s1, s2)", setup=setup, number = 100)
    8.027034027221656
    timeit.timeit("cmp2(s1, s2)", setup=setup, number = 100)
    8.175071701324946
    timeit.timeit("cmp3(s1, s2)", setup=setup, number = 100)
    14.243422195893174
    timeit.timeit("cmp4(s1, s2)", setup=setup, number = 100)
    5.0937542822775015

此外,当字符串大小很小并且它们实际上具有相同的字符时,David 的解决方案会脱颖而出。

编辑:更新测试结果

【讨论】:

  • 你可以使用d, d2 = dict.from_keys(str1), dict.from_keys(str2) 然后你就不必在循环中做in 测试
【解决方案4】:

这是@Joowani 解决方案的一个变体,它只使用一个字典并且运行得更快(至少在我的机器上):

def cmp4(str1, str2):
    if len(str1) != len(str2):
        return False
    d = collections.defaultdict(int)
    for c in str1:
        d[c] += 1
    for c in str2:
        d[c] -= 1
    return all(v == 0 for v in d.itervalues())

【讨论】:

  • 太棒了! +10 我已经确认这在我的机器上也运行得更快。
【解决方案5】:

这是一种不同的方式。通过使用我们忽略最多的“集合”:

if len(set(str1) - set(str2)) == 0:
    print "Yes"

【讨论】:

    猜你喜欢
    • 2022-12-01
    • 2016-04-10
    • 1970-01-01
    • 1970-01-01
    • 2017-02-09
    • 2015-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多