Разделите буквы разницей двух строк

Это поведение, которое я хочу:

a: IGADKYFHARGNYDAA c: KGADKYFHARGNYEAA 2 difference(s). 

Я предполагаю, что этот exmaple будет работать для вас в этом конкретном случае без особых сложностей и проблем с совместимостью с вашей версией программного обеспечения на python (до версии 2.7):

 a='IGADKYFHARGNYDAA' b='KGADKYFHARGNYEAA' u=zip(a,b) d=dict(u) x=[] for i,j in d.items(): if i==j: x.append('*') else: x.append(j) print x - a='IGADKYFHARGNYDAA' b='KGADKYFHARGNYEAA' u=zip(a,b) d=dict(u) x=[] for i,j in d.items(): if i==j: x.append('*') else: x.append(j) print x 

Выходы: ['*', 'E', '*', '*', 'K', '*', '*', '*', '*', '*']


С небольшими тиражами вы можете получить то, что хотите … Скажите, если это поможет 🙂


Обновить

Вы также можете использовать это:

 a='IGADKYFHARGNYDAA' b='KGADKYFHARGNYEAA' u=zip(a,b) for i,j in u: if i==j: print i,'--',j else: print i,' ',j - a='IGADKYFHARGNYDAA' b='KGADKYFHARGNYEAA' u=zip(a,b) for i,j in u: if i==j: print i,'--',j else: print i,' ',j 

Выходы:

 IK G -- G A -- A D -- D K -- K Y -- Y F -- F H -- H A -- A R -- R G -- G N -- N Y -- Y DE A -- A A -- A 

Обновление 2

Вы можете изменить код следующим образом:

 y=[] counter=0 for i,j in u: if i==j: print i,'--',j else: y.append(j) print i,' ',j print '\n', y print '\n Length = ',len(y) 

Выходы:

 IK G -- G A -- A D -- D K -- K Y -- Y F -- F H -- H A -- A R -- R G -- G N -- N Y -- Y DE A -- A AX ['K', 'E', 'X'] Length = 3 
 def diff_letters(a,b): return sum ( a[i] != b[i] for i in range(len(a)) ) 

Теория

  1. Итерации по обеим строкам одновременно и сравнение символов.
  2. Сохраните результат с помощью новой строки, добавив либо пробел, либо символ | характер к нему, соответственно. Кроме того, увеличьте целочисленное значение, начиная с нуля для каждого другого символа.
  3. Выведите результат.

Реализация

Вы можете использовать встроенную функцию zip или itertools.izip для одновременной итерации по обеим строкам, в то время как последняя является немного более эффективной в случае огромного ввода. Если строки не имеют одинакового размера, то итерация будет выполняться только для более короткой части. Если это так, вы можете пополнить остальные символом индикации отсутствия соответствия.

 import itertools def compare(string1, string2, no_match_c=' ', match_c='|'): if len(string2) < len(string1): string1, string2 = string2, string1 result = '' n_diff = 0 for c1, c2 in itertools.izip(string1, string2): if c1 == c2: result += match_c else: result += no_match_c n_diff += 1 delta = len(string2) - len(string1) result += delta * no_match_c n_diff += delta return (result, n_diff) 

пример

Вот простой тест, с немного отличающимися вариантами, чем от вашего примера выше. Обратите внимание, что я использовал символ подчеркивания для указания несогласованных символов, чтобы лучше продемонстрировать, как результирующая строка расширяется до размера более длинной строки.

 def main(): string1 = 'IGADKYFHARGNYDAA AWOOH' string2 = 'KGADKYFHARGNYEAA W' result, n_diff = compare(string1, string2, no_match_c='_') print "%d difference(s)." % n_diff print string1 print result print string2 main() 

Вывод:

 niklas@saphire:~/Desktop$ python foo.py 6 difference(s). IGADKYFHARGNYDAA AWOOH _

_

_|___ KGADKYFHARGNYEAA W

У Python есть отличный difflib , который должен обеспечить необходимую функциональность.

Вот пример использования из документации:

 import difflib # Works for python >= 2.1 >>> s = difflib.SequenceMatcher(lambda x: x == " ", ... "private Thread currentThread;", ... "private volatile Thread currentThread;") >>> for block in s.get_matching_blocks(): ... print "a[%d] and b[%d] match for %d elements" % block a[0] and b[0] match for 8 elements a[8] and b[17] match for 21 elements a[29] and b[38] match for 0 elements 

С difflib.ndiff вы можете сделать это в однострочном лайнере, который все еще несколько понятен:

 >>> import difflib >>> a = 'IGADKYFHARGNYDAA' >>> c = 'KGADKYFHARGNYEAA' >>> sum([i[0] != ' ' for i in difflib.ndiff(a, c)]) / 2 2 

( sum работает здесь, потому что, ну, вид True == 1 и False == 0 )

Следующее дает понять, что происходит и почему требуется / 2 :

 >>> [i for i in difflib.ndiff(a,c)] ['- I', '+ K', ' G', ' A', ' D', ' K', ' Y', ' F', ' H', ' A', ' R', ' G', ' N', ' Y', '- D', '+ E', ' A', ' A'] 

Это также хорошо работает, если строки имеют разную длину.

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

 a = 'IGADKYFHARGNYDAA' b = 'KGADKYFHARGNYEAA' counter = 0 differences = 0 for i in a: if i != b[counter]: differences += 1 counter += 1 

Здесь каждый раз, когда мы сталкиваемся с буквой в последовательности а, которая отличается от буквы в том же положении в последовательности b, мы добавляем 1 к «различиям». Затем мы добавляем 1 к счетчику, прежде чем переместимся на следующую букву.

Мне нравится ответ от Niklas R, но у него есть проблема (в зависимости от ваших ожиданий). Используя ответ со следующими двумя тестовыми примерами:

 print compare('berry','peach') print compare('berry','cherry') 

Мы можем разумно ожидать, что вишня будет больше похожа на ягоды, чем на персик . Тем не менее, мы получаем более низкий разницу между ягодами и персиком , затем ягодными и вишневыми :

 (' | ', 4) # berry, peach (' | ', 5) # berry, cherry 

Это происходит, когда строки более похожи назад, чем вперед. Чтобы продлить ответ от ответа от Niklas R, мы можем добавить вспомогательную функцию, которая возвращает минимальный разность между нормальным (вперед) diff и разностью обратных строк:

 def fuzzy_compare(string1, string2): (fwd_result, fwd_diff) = compare(string1, string2) (rev_result, rev_diff) = compare(string1[::-1], string2[::-1]) diff = min(fwd_diff, rev_diff) return diff 

Повторите следующие тесты:

 print fuzzy_compare('berry','peach') print fuzzy_compare('berry','cherry') 

… и мы получаем

 4 # berry, peach 2 # berry, cherry 

Как я уже сказал, это действительно просто расширяет , а не изменяет ответ Никласа Р.

Если вы просто ищете простую функцию diff (с учетом вышеупомянутой gotcha), то следующее:

 def diff(a, b): delta = do_diff(a, b) delta_rev = do_diff(a[::-1], b[::-1]) return min(delta, delta_rev) def do_diff(a,b): delta = 0 i = 0 while i < len(a) and i < len(b): delta += a[i] != b[i] i += 1 delta += len(a[i:]) + len(b[i:]) return delta 

Тестовые случаи:

 print diff('berry','peach') print diff('berry','cherry') 

Последнее соображение касается самой функции diff при обработке слов различной длины. Существует два варианта:

  1. Рассмотрим разницу между длинами как разностными символами.
  2. Игнорируйте разницу в длине и сравните только кратчайшее слово.

Например:

  • яблоко и яблоки имеют разницу в 1 при рассмотрении всех персонажей.
  • яблоко и яблоки имеют разницу 0 при рассмотрении только кратчайшего слова

Рассматривая только кратчайшее слово, которое мы можем использовать:

 def do_diff_shortest(a,b): delta, i = 0, 0 if len(a) > len(b): a, b = b, a for i in range(len(a)): delta += a[i] != b[i] return delta 

… число итераций продиктовано самым коротким словом, все остальное игнорируется. Или мы можем принимать во внимание разную длину:

 def do_diff_both(a, b): delta, i = 0, 0 while i < len(a) and i < len(b): delta += a[i] != b[i] i += 1 delta += len(a[i:]) + len(b[i:]) return delta 

В этом примере любые оставшиеся символы подсчитываются и добавляются к значению diff. Чтобы проверить обе функции

 print do_diff_shortest('apple','apples') print do_diff_both('apple','apples') 

Вывод:

 0 # Ignore extra characters belonging to longest word. 1 # Consider extra characters. 
 a = "IGADKYFHARGNYDAA" b = "KGADKYFHARGNYEAAXXX" match_pattern = zip(a, b) #give list of tuples (of letters at each index) difference = sum (1 for e in zipped if e[0] != e[1]) #count tuples with non matching elements difference = difference + abs(len(a) - len(b)) #in case the two string are of different lenght, we add the lenght difference 

Вот мое решение аналогичной проблемы, сравнивая две строки на основе представленного здесь решения: https://stackoverflow.com/a/12226960/3542145 .

Поскольку itertools.izip не работал для меня в Python3, я нашел решение, которое вместо этого использует zip-функцию: https://stackoverflow.com/a/32303142/3542145 .

Функция сравнения двух строк:

 def compare(string1, string2, no_match_c=' ', match_c='|'): if len(string2) < len(string1): string1, string2 = string2, string1 result = '' n_diff = 0 for c1, c2 in zip(string1, string2): if c1 == c2: result += match_c else: result += no_match_c n_diff += 1 delta = len(string2) - len(string1) result += delta * no_match_c n_diff += delta return (result, n_diff) 

Настройте две строки для сравнения и вызовите функцию:

 def main(): string1 = 'AAUAAA' string2 = 'AAUCAA' result, n_diff = compare(string1, string2, no_match_c='_') print("%d difference(s)." % n_diff) print(string1) print(result) print(string2) main() 

Что возвращает:

 1 difference(s). AAUAAA 

_|| AAUCAA