Эффективно найти повторяющиеся символы в строке

Я знаю, что эффективность этого кода не является оптимальной (особенно с гигантскими входами), и я знаю, что есть способ изменить этот алгоритм для обработки других типов данных, а не просто повторения в строке (очевидно, там для поиска достаточно всего нескольких символов).

Есть ли способ увеличить эффективность здесь?

Я пробовал использовать словарь, и функция продолжала возвращать "none", поэтому я попробовал список, и все получилось отлично.

Спасибо заранее любому, кто может мне помочь!

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')  

Ответ 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 или Counter, возвращается сразу после обнаружения повтора и не нуждается в предварительной обработке всего списка. Вы также должны использовать set, если вам нужен первый повторяющийся элемент, а не только один из них.

Counter - хороший инструмент, но он не предназначен для производительности, поэтому, если вам действительно нужно иметь дело с "гигантскими входами", пойдите с наборами (если они вписываются в память) или слиянием, если они этого не делают.

Ответ 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

Ответ 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

Ответ 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')  

Ответ 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)