2016-10-10 3 views
1

Я изо всех сил пытаюсь решить эту проблему (и как придумать имя для этого вопроса в stackoverflow).список строк - удалить общие черты общих строк

Мне нужно как-то удалить общности строк, сохраняя остаток.

Учитывая список таких как:

l = ('first', 
    'first.second', 
    'first.second.third', 
    'a.b.c', 
    'x.y.z', 
    'x.y') 

Я надеюсь иметь выход список как:

('first', 
'second', 
'third', 
'a.b.c', 
'x.y', 
'z') 

, как вы можете видеть, когда first.second вычитает first и остается second. first.second вычитается из first.second.third, мы получаем third. a.b.c не имеет ничего, чтобы вычесть из себя, поэтому он остается. x.y вычитается из x.y.z и z остается.

Я думаю, что, возможно, sort(key=len) будет частью решения, а также какой-то рекурсии, чтобы в итоге получить остаток строки. Я надеюсь на чистый способ удалить общие черты каждой строки в списке.

+0

может посмотреть на различные "наибольшую общую подпоследовательность" вопросы здесь. –

+0

Похоже, что список проходит по порядку, а операнды - только последовательные, это правильно? – sal

+0

это почти как «противоположность квалификации» .... как почти исключая пространства имен (я не делаю этого, но это почти то, что мне кажется). – joefromct

ответ

1

Я думал об этой интересной проблемы еще немного и придумал решение.

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

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

Я буду использовать следующий список расширенного списка слов, так как он указывает на некоторые крайние случаи, которые делают другие решения не:

li = ['First', 
     'FirstSecond', 
     'FirstSecondFirst', 
     'FirstSecondFirstly', 
     'FirstSecondThird', 
     'FirstFoo', 
     'FirstBarracudaSphyraena', 
     'xyz', 
     'xy', 
     '12345', 
     '123', 
     '1', 
     'FireTruckGarage', 
     'FireTruck', 
     'Fire'] 

Хитрость заключается в заметив, что каждый раз, когда есть потенциал удлинение префикса, мы должны сохранить предыдущий префикс в стеке префикса (здесь prefixes), который содержит все префиксы, которые до сих пор не исчерпаны. После того, как мы сделали некоторые из «более глубоких» слов - в смысле того, чтобы быть узлами глубже по дереву, нам может потребоваться отступить и снова использовать старый префикс для более короткого слова.

После того, как встречается слово, которое не префикс текущего префикса, мы должны выставить стек префиксов, пока мы не достигнем того, что делает префикс слова и продолжить оттуда.

def ambiguate(words): 
    output = {} 
    prefixes = [''] 
    prefix = prefixes[0] 

    for item in sorted(set(words)): 
     backtracked = False 

     while not item.startswith(prefix): 
      prefix = prefixes.pop() 
      backtracked = True 

     # If we have backtracked, we put back the current prefix onto the 
     # prefix stack since we may have to use it later on. 
     if backtracked: 
      prefixes.append(prefix) 

     # Construct new output and a new prefix and append it to the 
     # prefix stack 
     output[item] = item.replace(prefix, '', 1) 
     prefix = item 
     prefixes.append(prefix) 

    return output 

Продолжительность:

print(ambiguate(li)) 

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

{'1': '1',          
'123': '23',          
'12345': '45',         
'Fire': 'Fire',         
'FireTruck': 'Truck',       
'FireTruckGarage': 'Garage',      
'First': 'First',        
'FirstBarracudaSphyraena': 'BarracudaSphyraena', 
'FirstFoo': 'Foo',        
'FirstSecond': 'FirstSecond',     
'FirstSecondFirst': 'First',      
'FirstSecondFirstly': 'ly',      
'FirstSecondFourth': 'Fourth',     
'FirstSecondThird': 'FirstSecondThird',   
'a': 'a',          
'abc': 'bc',          
'xy': 'xy',          
'xyz': 'z'} 
+0

Это выглядит великолепно, большое вам спасибо. Я собираюсь сделать дополнительное тестирование, а затем вернусь, чтобы принять его в качестве ответа. – joefromct

+0

Отличный ответ (+1). Обратите внимание, что существует целый ряд крайних случаев, которые вы на самом деле не рассматриваете: например, 'li = ('apple', 'application.apple')'. Если вы не используете обработку естественного языка или не имеете искусственного способа разделения членов (например, периодов), я не думаю, что вы можете заставить это работать для подобных случаев. – brianpck

+0

@brianpck Спасибо. Если я правильно понял joefromct, проблема действительно в том, что касается произвольных строк и префиксов и не имеет ничего общего с фактическими словами естественного языка и периодами. Он упоминает [здесь] (https://stackoverflow.com/questions/39963368/list-of-strings-remove-commonalities-of-common-strings/39988612?noredirect1_comment67219044_39963718), что он использовал периоды только для ясности. Из его правил '('apple', 'application.apple')' должен просто предоставить одну и ту же коллекцию, и здесь это делает код. – dkasak

2

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

  1. «Участники» разделены по периодам: тот же «член» не может отображаться в двух элементах кортежа.
  2. Каждый член должен появляться только один раз.

Проблема заключается в том, что приоритет неоднозначен. Например, в следующей последовательности:

lst = ('a.b.c', 
     'a.b.d') 

Где находятся «а» и «b»? В вашем тестовом примере подразумевается, что общий член должен перейти на тот, у которого есть наименее общие члены (так что «z» не остается с «x.y.z»), но есть много крайних случаев, которые необходимо учитывать. Вам нужно будет указать свои требования в более точном формате.

Принятие гораздо более простое правило, что «член» должен оставаться в первую очередь, что кажется, следующая функция делает трюк:

def remove_common(lst): 
    seen = set() 
    res = [] 
    for i in lst: 
     members = i.split('.') 
     res.append('.'.join(w for w in members if w not in seen)) 
     seen |= set(members) 
    return tuple(res) 

Это дает результат очень близкий к вашему:

>>> remove_common(l) 
('first', 'second', 'third', 'a.b.c', 'x.y.z', '') 
+0

На самом деле, члены на самом деле не ограничены периодами, я использовал это, чтобы попытаться быть ясным. Это могут быть слова, такие как «Fire», «FireTruck» и «FireTruckGarage». Мне хотелось бы получить такие данные, как «Fire», «Truck» и «Garage» («Fire» остается, ничего не вычитать. «FireTruck» имеет «Пожар» вычитал, оставив грузовик.'FireTruckGarage' имеет' FireTruck', вычитает оставить 'Garage'.) И да, мне понадобится один вывод для каждого элемента списка в списке. – joefromct

+0

Я думаю, что решение этого будет довольно сложным, если вы хотите, чтобы оно было эффективным, и если нет простого способа токенизировать строки в «сегменты» (например, с периодами). Есть ли какие-либо ограничения на строки, которые позволяли бы использовать этот вид токенизации, или они могут быть действительно произвольными? – dkasak

2

Если выходной порядок не важен, это решение даст вам ожидаемые значения.

Это реализация почти такая же, как ответ на @ brianpck. Но я использовал сортировку для решения проблемы "x.y.z". И некоторые дополнительные объяснения.

l = ('first', 
    'first.second', 
    'first.second.third', 
    'a.b.c', 
    'x.y.z', 
    'x.y') 

def clarify(lst): 

    # Sort the list to deal with the order problem. 
    # for ex. "x.y.z" deletes "x.y" if not sorted 
    lst = sorted(lst) 

    # Words encountered. 
    words = set() 

    new_list = [] 

    for elem in lst: 

     # Divide elements using dots. 
     divided = elem.split(".") 

     # New element to be added to the result. 
     new_elem = [] 

     # For each word in a divided element. 
     for word in divided: 
      # If a word in the element is not encountered before. 
      # Add it to new element 
      # Add it to words 
      if word not in words: 
       new_elem.append(word) 
       words.add(word) 
     # Join new element 
     new_list.append(".".join(new_elem)) 

    return new_list 

print clarify(l) 

# Gives ['a.b.c', 'first', 'second', 'third', 'x.y', 'z'] 
# You can make this a tuple in the solution as in the input if you want. 
+0

Это действительно единственный шанс, что это сработает: если вы замените третий элемент в 'l' на' ''a.first.second.third'', результатом будет '['abc', 'first.second.third. четвертый ',' ',' ',' ',' x.y ',' z '] 'Опять же, это сводится к необходимости более четких требований. – brianpck

+0

@brianpck Вы правы. Мое решение заключалось в предположении, что существуют разные пакеты элементов, которые не пересекаются друг с другом. Таким образом, нам нужны более четкие требования. – Rockybilly

0

Я думаю, что, может быть, я тоже пытался слишком поучиться со списком и пониманием диктата.

Я считаю, что я был в состоянии решить эту проблему следующим:

  1. сортировать в переплете список
  2. использовать переменную для «предыдущего элемента списка»
  3. цикла, и выход на текущий элемент замены предыдущего элемента (если он найден)

Вот что я до сих пор:

li = [ 
    'first', 
    'first.second', 
    'first.second.third', 
    'a', 
    'a.b', 
    'a.b.c', 
    'x.y.z', 
    'x.y'] 

li.sort() 

prev_l = '' 
output = {} 
for l in li: 
    if l.find(prev_l) ==0: 
     output[l] = l.replace(prev_l,'') 
    else: 
     output[l] = l 
    prev_l = l 

выход:

{ 
'a'     : 'a', 
'a.b'    : '.b', 
'a.b.c'    : '.c', 
'first'    : 'first', 
'first.second'  : '.second', 
'first.second.third' : '.third', 
'x.y'    : 'x.y', 
'x.y.z'    : '.z'} 
+0

n/m, это не работает с вводом, например 'a.b.c' и' a.b.d'. Я хочу, чтобы результат был '.c', а также' .d'. – joefromct

+0

Вы должны добавить больше тестовых примеров на свой вопрос, так как это удивительно. Из всех ваших других тестовых примеров кажется, что 'a.b.c' будет только« разделяться »своими префиксами (так' a.b' или 'a'), но не' a.b.d', который не является префиксом. – dkasak

+0

'a.b' разделит' a.b.d'. 'a.b' является префиксом как' a.b.c', так и 'a.b.d'. – joefromct

Смежные вопросы