【问题标题】:Efficiently find repeated characters in a string有效地查找字符串中的重复字符
【发布时间】:2014-10-31 14:28:21
【问题描述】:

我知道这段代码的效率不是最优的(尤其是有巨大的输入),而且我知道有一种方法可以改变这个算法来处理其他数据类型,而不仅仅是字符串中的重复(显然有只有这么多字符可供搜索)。

有什么办法可以提高效率吗?

我尝试使用字典,但函数一直返回“无”,所以我尝试了一个列表,结果很好。

提前感谢任何可以帮助我的人!

def find_repeater(string):
    my_list = []
    my_list.append(string[0])

    for i in range (1, len(string)):

        if string[i] in my_list:
            print 'repetition found'
            return (string[i])

        else:
            my_list.append(string[i])

print find_repeater('abca')  

现在有了字典....(它一直在控制台打印“无”)

def find_repeater(string):
    my_dict = {}
    my_dict[0] = string[0]

    for i in range (1, len(string)):

        if string[i] in my_dict:
            print 'repetition found'
            return string[i]

        else:
            my_dict[i] = string[i]

print find_repeater('abca')  

【问题讨论】:

  • 使用字典是要走的路。展示您的尝试,我们会帮助您解决问题。
  • 如果有多个重复的字母怎么办?
  • @PadraicCunningham:看起来 OP 想要返回首先重复的字母
  • 可以先对输入进行排序吗?
  • 如果您有工作代码,但效率不够,代码审查可能是一门很好的课程(目前还不是)。但无论如何,请考虑使用适当的标题来描述您需要帮助的问题,而不仅仅是呼救。尽管您只希望在正确的方向上稍微戳一下而不是整个解决方案,但值得称赞。

标签: python string performance


【解决方案1】:

由于这是一个性能问题,让我们做一些时间安排:

def test_set(xs):
    seen = set()  # O(1) lookups
    for x in xs:
        if x not in seen:
            seen.add(x)
        else:
            return x

import collections

def test_counter(xs):
    freq = collections.Counter(xs)
    for k in freq:
        if freq[k] > 1:
            return k

def test_dict(xs):
    d = {}
    for x in xs:
        if x in d:
            return x
        d[x] = 1

def test_sort(xs):
    ys = sorted(xs)

    for n in range(1, len(xs)):
        if ys[n] == ys[n-1]:
            return ys[n]

##

import sys, timeit
print (sys.version + "\n")
xs = list(range(10000)) + [999]
fns = [p for name, p in globals().items() if name.startswith('test')]
for fn in fns:
    assert fn(xs) == 999
    print ('%50s %.5f' % (fn, timeit.timeit(lambda: fn(xs), number=100)))

我正在测试一个整数列表而不是一个字符串(因为使用字符串你不能得到超过 256 个循环)。我的机器上的结果如下所示:

3.2.3 (v3.2.3:3d0686d90f55, Apr 10 2012, 11:25:50) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]

                <function test_set at 0x1020f7380> 0.19265
               <function test_dict at 0x1020f7490> 0.12725
               <function test_sort at 0x1020f7518> 0.04683
            <function test_counter at 0x1020f7408> 0.92485

所以排序方法似乎是赢家。我想这是因为它不会浪费时间创建哈希和分配 dict/set 结构。此外,如果您不关心源列表被更改,您可以使用 xs.sort() 而不是 ys = sorted(xs),这样可以实现零内存占用。

另一方面,如果重复项更有可能出现在输入的开头(如xs = 'abcdef' * 10000),则set 方法将执行得最好,因为它与sort 或@987654328 不同@,一旦发现重复就立即返回,不需要预处理整个列表。如果你需要 first 重复元素,你也应该使用set,而不仅仅是其中一个。

Counter 是一个不错的工具,但它不是为性能而设计的,所以如果你真的必须处理“巨大的输入”,请使用集合(如果它们适合内存)或合并排序,如果它们不适合。

【讨论】:

  • 在这个例子中排序不会有优势,因为你已经有一个列表,除了一个值之外,所有的值都被排序了?在设置 xs 之后放置 random.shuffle(xs) 会使排序表现最差,或者与计数器并列。
【解决方案2】:

您可以使用collections 查找重复字符:

import collections

freq = collections.Counter("abcda")
for k in freq:
    if freq[k] > 1:
        print k # prints "a"
        break

如果只想查找是否有重复(不查找重复字符):

letters = list("abcda")
no_rep = set(letters)
print len(letters) > len(no_rep) # prints 'True' when there are repeating characters

【讨论】:

  • 不要认为您需要将集合转换为列表或使字母成为列表,letters = ("abcda") len(letters) &gt; len(set(letters))
  • @PadraicCunningham 对! (thx - 已修复),我忘了删除它(在发布之前正在玩其他选项)。
  • 不用担心,+1 是,我也打算发布一个 Counter 答案,只是太懒了 :)
【解决方案3】:
def find_repeater(mystr):
    seen = set()  # O(1) lookups
    for char in mystr:
        if char not in seen:
            seen.add(char)
        else:
            print('repetition found')
            return char

【讨论】:

  • OP 要求提供字典。应该像我一样给出两个答案
【解决方案4】:

给你,用字典:

def find_repeater(string):
    my_list = {}
    my_list[string[0]] = 1
    for i in range (1, len(string)):
        if string[i] in my_list.keys():
            print 'repetition found'
            return (string[i])
        else:
            my_list[string[i]] = 1
print find_repeater('abca')  

不过,我个人会在这里使用一组:

def find_repeater(string):
    my_list = set()
    my_list.add(string[0])
    for i in range (1, len(string)):
        if string[i] in my_list:
            print 'repetition found'
            return (string[i])
        else:
            my_list.add(string[i])
print find_repeater('abca')  

【讨论】:

  • 为什么要使用 range 而不是只遍历字符串?
【解决方案5】:

这应该可行。

def return_dupe(string):
    d = {}
    [d.update({k:2}) if k in d else d.update({k:1}) for k in string]
    return [key for key in d if d[key] > 1]

【讨论】:

    【解决方案6】:

    另一种方法可以很简单:

    def repeting_count(text):
    
        working_text = str(text.lower())
    
        dup = []
    
        for char in working_text:
            if working_text.count(char) > 1:
                dup.append(char)
    
            else:
                pass
    
        for num in dup:
            while dup.count(num) > 1:
                dup.remove(num)
    

    【讨论】:

      【解决方案7】:
      def rec_char(s):
          buf={}
          for c in s:
              try:
                  print(buf[c],end=", ")
              except:
                  buf[c]=c
      
      
      rec_char("ABCAB")
      

      python3中这个时间复杂度为O(n)的函数会打印出现多次的字符。

      【讨论】:

        猜你喜欢
        • 2017-01-14
        • 2019-07-16
        • 1970-01-01
        • 2019-02-17
        • 2023-03-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多