Как найти неупорядоченные символы в строке

1

Этот вопрос относится к текущей проблеме, с которой я столкнулся на работе, но поскольку она довольно широкая, я пытаюсь сформулировать это как более вопрос интервью, чтобы поощрять обсуждение.

Скажем, мы имеем следующие две строки:

str1 = 'helloworld'
str2 = 'helloldwor'

Я хочу, чтобы иметь возможность сравнивать str1 с str2 и определять, какие символы в str2 не соответствуют порядку, используя предположение, что str1 является "правильным". Также можно предположить, что все символы в str2 являются такими же, как str1 (str2 - просто перепутанная версия str1).

EDIT: В этом случае я бы сказал, что "ld" не работает. Я определяю подстроку "не в порядке" как наименьшую подстроку str2, которая при перемещении в то же место, что и подстрока в str1, сделает str1 == str2.

Этот вопрос долго меня беспокоил, так как это легко понять визуально, но я изо всех сил пытаюсь превратить его в какой-то алгоритм.

Моя попытка:

def get_ooo(str1, str2):
#for potential options
local_set = Set()

#Loop from len(str1) to 1, splitting str2 by i to cover all possible substrings of str2
split_size = len(str1)
for i in range(len(str1),1,-1):
    print 'Iteration #' + str(len(str1) - split_size)

    #Try to find all substrings of str2 of length 'i' in str1
    for j in range(0,len(str1)-i):
        if str1.find(mid(str2,j,i)) < 0:
            #Failed to find substring in str1

            #Add to our local_set if it is a substring of all other failed substrings
            intersect = True
            for k in local_set:
                if k.find(mid(str2,j,i)) < 0:
                    intersect = False

            #If substring was a substring of all other failed substrings
            if intersect:
                #Add to local_set
                local_set.add(mid(str2,j,i))
                print mid(str2,j,i) + ' - FAIL, PASS'
            else:
                print mid(str2,j,i) + ' - FAIL, FAIL'
        else:
            print mid(str2,j,i) + ' - PASS'

#solution found?
best_option = ''
for option in local_set:
    if len(option) < len(best_option) or best_option == '':
        best_option = option
return best_option

По сути, логика, которую я использую, - это поиск подстрок str2 в str1, начиная с самых больших возможных подстрок. Когда я нахожу тот, который не подходит, я добавляю его к возможному набору решений. И если я нахожу другую подстроку, которая не подходит для str1, я добавляю ее только к возможным параметрам, если она также является подстрокой всех других возможных вариантов. Поэтому подстрока наименьшего размера в этом наборе в конце будет содержать первый символ вне порядка.

Поэтому, используя этот алгоритм, я всегда знаю, где начинается секция вне порядка . , Тем не менее, я не знаю, как на самом деле извлечь раздел, который вышел из строя.

Я попытался передать строки в функцию в обратном порядке, которая дает мне первый экземпляр символа из строки со спины, которая затем дает мне полную нестандартную подстроку здесь. Но что, если есть несколько разделов, которые вышли из строя? Кроме того, основанный на моем тестировании, этот скрипт возвращает только первый экземпляр строки str2, что подстрока не соответствует порядку. Например:

str1 = 'helloworld'
str2 = 'hworldello'

Вернет "hw", сообщив, что "w" - это место, где строка выходит из строя. Но в этом примере было бы более разумно, если бы "ello" было не в порядке, а не "мировая" подстрока, поскольку она больше.

Я смотрел на этот вопрос уже более дня и решил время, чтобы открыть его для других мнений, тем более, что я чувствую, что должен быть лучший способ. Так что вы все думаете? У кого-нибудь есть блестящие идеи?

  • 1
    Вы можете прочитать о расстоянии Левенштейна en.wikipedia.org/wiki/Levenshtein_distance
  • 0
    Я думаю, что вы должны начать с формального определения того, что вы подразумеваете под "из строя".
Показать ещё 1 комментарий
Теги:
string
sorting

1 ответ

0
Лучший ответ

Вы можете использовать рекурсию и поиск по строке как вперед, так и назад, чтобы гарантировать возврат минимальной подстроки:

def find_substring_forwards(str1, str2, storage, index):

    global main1, main2

    for i, (x, y) in enumerate(zip(str1, str2)):
        if x!=y and not storage: index = i
        expected_letter = main1[main1.index(''.join(storage))+len(storage)]
        if (x!=y or (expected_letter==x and main1.count(expected_letter)>1)) and index==i:
            storage.append(y)
            str2 = str2[:i] + str2[i+1:]
            if str1[:len(str2)]==str2: break
            return find_substring_forwards(str1, str2, storage, index)

    return ''.join(storage)

def find_substring_backwards(str1, str2, storage, index):

    global main1, main2

    for i, (x, y) in enumerate(zip(str1, str2)):
        if x!=y and not storage: index = i
        if x!=y and index==i:
            storage.append(y)
            str2 = str2[:i] + str2[i+1:]
            if str1[:len(str2)]==str2: break
            return find_substring_backwards(str1, str2, storage, index)

    return ''.join(storage)

def out_of_order(str1, str2):

    x = ''.join(find_substring_forwards(str1, str2, [], None))
    y = ''.join(find_substring_backwards(str1[::-1], str2[::-1], [], None)[::-1])
    final = x if len(x)<=len(y) else y

    return final

Несколько тестовых случаев:

test_cases = [('helloworld','heworldllo','llo'),
            ('helloworld','hwoellorld','wo'),
            ('helloworld','hworldello','ello'),
            ('helloworld','helloldwor','ld'),
            ('helloworld','helloowrld','o'),
            ('helloworld','whelloorld','w')]

for test in test_cases:
    main1 = test[0]; main2 = test[1]
    x = out_of_order(main1, main2)
    print(main1, '|', main2)
    print('Expected:', test[2], '| Returned:', x, '\n')

Урожайность:

helloworld | heworldllo
Expected: llo | Returned: llo 

helloworld | hwoellorld
Expected: wo | Returned: wo 

helloworld | hworldello
Expected: ello | Returned: ello 

helloworld | helloldwor
Expected: ld | Returned: ld 

helloworld | helloowrld
Expected: o | Returned: o 

helloworld | whelloorld
Expected: w | Returned: w 

Объяснение:

Мы одновременно итерации по обеим строкам, зная, что str1 - искомая строка. Когда мы найдем письмо, которое не соответствует правильной позиции из str1, добавим это письмо в хранилище и обратите внимание на индекс. Затем мы удаляем эту букву из строки и повторяем процесс. Мы продолжаем этот рекурсивный цикл до тех пор, пока не изменится индекс, в котором мы удаляем буквы. Это указывает на то, что мы достигли конца подстроки "вне порядка". Чтобы убедиться, что мы находим минимальную подстроку (в терминах символов), мы должны выполнить тот же метод в обратном порядке (итерация назад по строке). В функции out_of_order мы просто берем меньшую из двух подстрок, и если они равны, то мы принимаем решение от итерации вперед по строкам (так как либо технически корректно).

Обновить

Имел предыдущий выпуск с повторяющимися буквами в строке для следующего тестового примера:

('helloworld','heworldllo','llo')

Алгоритм теперь проверяет, является ли текущая буква кандидатом на удаление из строки, на самом деле является ожидаемой буквой для подстроки вне порядка. Если это так, то добавьте в контейнер storage подстроки вне порядка, а не до конца поиска подстроки преждевременно. Также отделяются функции для итерации вперед и назад для ясности и удобочитаемости.

  • 0
    Спасибо за ответ! Я думаю, что это работает довольно хорошо, единственная проблема в случаях, когда символы из каждой строки совпадают, но мы все еще читаем фрагмент «вне очереди». Например, out_or_order ('helloworld', 'heworldllo') вернет 'wor'. Однако это все еще работает для меня, поскольку у «строк», над которыми я работаю, есть уникальные персонажи, так что это никогда не должно попадать в этот случай. Ради проблемы, я сейчас изучаю способы работы с неуникальными символами для этого и обновлю, если найду что-нибудь.
  • 0
    Большой! Был бы признателен upvote и галочку, если это помогло!
Показать ещё 6 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню