字符串列表 - 删除常见字符串的共性

时间:2016-10-10 17:09:22

标签: python algorithm

我正在努力想出办法解决这个问题(以及如何在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减去thirda.b.c没有任何东西要从中减去,所以它仍然存在。从x.y中扣除x.y.zz仍然存在。

我想,也许sort(key=len)将成为解决方案的一部分,以及某种递归结束字符串余数。我希望有一种干净的方法来删除列表中每个字符串的共性。

4 个答案:

答案 0 :(得分:2)

我认为在编写解决方案之前,您需要更准确地定义问题。这是我从您的测试用例中推断的内容:

  1. “成员”由句点分隔:相同的“成员”不能出现在两个元组中。
  2. 每个成员只应出现一次。
  3. 但问题是优先级是不明确的。例如,按以下顺序:

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

    'a'和'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', '')
    

答案 1 :(得分: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.

答案 2 :(得分: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'}

答案 3 :(得分:0)

我想也许我对列表和词典理解太过花哨了。

我相信我能用以下方法解决问题:

  1. 排序入境列表
  2. 使用变量“previous list element”
  3. 循环,并输出当前元素替换前一个元素(如果找到)
  4. 这是我到目前为止所拥有的:

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