在Python

时间:2018-06-04 12:36:06

标签: python python-3.x algorithm list lcs

在某些NLP任务中,我有一个嵌套的字符串列表:

    [['Start', 'двигаться', 'другая', 'сторона', 'света', 'надолго', 'скоро'], 
     ['Start', 'двигаться', 'другая', 'сторона', 'света', 'чтобы', 'посмотреть'],
     ['Start', 'двигаться', 'новая', 'планета'],
     ['Start', 'двигаться', 'сторона', 'признание', 'суверенитет', 'израильский'],
     ['Start', 'двигаться', 'сторона', 'признание', 'высот', 'на'],
     ['Start', 'двигаться', 'сторона', 'признание', 'высот', 'оккупировать'],
     ['Start', 'двигаться', 'сторона', 'признание', 'высот', 'Голанский'],
     ['Start', 'двигаться', 'сторона', 'признание', 'и']]

我需要一种算法来查找两个或更多元素,这些元素对于两个或更多子列表是常见的,并从中生成单个元素。在我的示例中,'Start', 'двигаться'对于所有元素都是通用的,因此它应该成为单个字符串。 'сторона', 'света', 'надолго'对于两个元素很常见,因此它成为单个字符串。 'сторона', 'признание'对于5个元素很常见,因此它变成单个字符串。如果没有剩余公共元素,只需将其余元素添加为单个字符串。 期望的输出:

    [['Start двигаться', 'другая сторона света', 'надолго скоро'], 
     ['Start двигаться', 'другая сторона света', 'чтобы посмотреть'],
     ['Start двигаться', 'новая планета'],
     ['Start двигаться', 'сторона признание', 'суверенитет израильский'],
     ['Start двигаться', 'сторона признание', 'высот на'],
     ['Start двигаться', 'сторона признание', 'высот оккупировать'],
     ['Start двигаться', 'сторона признание', 'высот Голанский'],
     ['Start двигаться', 'сторона признание', 'и']]

到目前为止,我尝试了一些循环和元素比较:

for elem,next_elem in zip(lst, lst[1:]+[lst[0]]):
    if elem[0] == next_elem[0] and elem[1] == next_elem[1] and elem[2] == next_elem[2]:
        elem[0:3] = [' '.join(elem[0:3])]

    if elem[0] == next_elem[0] and elem[1] == next_elem[1]:
        elem[0:2] = [' '.join(elem[0:2])]

但我不认为这是正确的方法。集合也不是一个选项,因为子列表中可能会出现多个元素。 我检查了其他LCS主题,但没有找到解决方案。任何完成工作的工作算法都会很棒,效率目前并不重要。还有一些例子:

[[a,b,c,d],
 [a,b,d,e,f]]

应该成为:

[[ab,cd],
 [ab,def]]

由于a,b是常见元素,cd, def只是单个元素。

[[a,b,c,d,e,g],
[a,b,c,d,g,h],
[a,b,h,h,i]]

应该成为:

[[ab,cd,eg],
 [ab,cd,gh],
 [ab,hhi]]

由于abcd是两个或多个子列表的大炮

[[a,b,c],
 [a,b,d]] 

变为:

[[ab, c],
 [ab, d]]

由于c, d不是常见元素

2 个答案:

答案 0 :(得分:1)

我建议你使用hashmaps键:word,value:整数作为计数器,从0开始。(这是python中的字典)。对于每一行,散列每个值并增加计数器。最后,对于每个具有2或更多计数器的单词,您将它们连接起来。

我遗漏了代码和你只用相同的计数器连接字符串的部分,以及重复,因为这似乎是家庭作业。

答案 1 :(得分:1)

您可以从创建代表您的列表的prefix-tree开始:

lists = [['Start', 'двигаться', 'другая', 'сторона', 'света', 'надолго', 'скоро'], 
         ['Start', 'двигаться', 'другая', 'сторона', 'света', 'чтобы', 'посмотреть'],
         ['Start', 'двигаться', 'новая', 'планета'],
         ['Start', 'двигаться', 'сторона', 'признание'],
         ['Start', 'двигаться', 'сторона', 'признание', 'суверенитет', 'израильский'],
         ['Start', 'двигаться', 'сторона', 'признание', 'высот', 'на'],
         ['Start', 'двигаться', 'сторона', 'признание', 'высот', 'оккупировать'],
         ['Start', 'двигаться', 'сторона', 'признание', 'высот', 'Голанский'],
         ['Start', 'двигаться', 'сторона', 'признание', 'и']]

tree = {}
end = "END"
for lst in lists:
    d = tree
    for x in lst:
        d = d.setdefault(x, {})
    d[end] = {}

结果(此处,END标记句子结束的位置):

{'Start': {'двигаться': {'другая': {'сторона': {'света': {'надолго': {'скоро': {'END': {}}},
                                                          'чтобы': {'посмотреть': {'END': {}}}}}},
                         'новая': {'планета': {'END': {}}},
                         'сторона': {'признание': {'END': {},
                                                   'высот': {'Голанский': {'END': {}},
                                                             'на': {'END': {}},
                                                             'оккупировать': {'END': {}}},
                                                   'и': {'END': {}},
                                                   'суверенитет': {'израильский': {'END': {}}}}}}}}

现在,您可以递归遍历该树,并且只要节点只有一个子节点(只有一个元素的子字典),就可以加入这些节点。

def join(d, pref=[]):
    if end in d:
        yield [' '.join(pref)] if pref else []
    for k, v in d.items():
        if len(v) == 1:
            for x in join(v, pref + [k]): # add node to prefix
                yield x                   # yield next segment
        else:
            for x in join(v, []):         # reset prefix
                yield [' '.join(pref + [k])] + x # yield node + prefix and next

输出不像你的问题那样完全,但非常接近。它将连接树中只有一个子节点的所有部分,即之后的段应该是最大的,而没有段是较长段的一部分。

>>> for x in join(tree):
...     print(x)
...
['Start двигаться', 'другая сторона света', 'надолго скоро']
['Start двигаться', 'другая сторона света', 'чтобы посмотреть']
['Start двигаться', 'новая планета']
['Start двигаться', 'сторона признание']
['Start двигаться', 'сторона признание', 'суверенитет израильский']
['Start двигаться', 'сторона признание', 'высот', 'на']
['Start двигаться', 'сторона признание', 'высот', 'оккупировать']
['Start двигаться', 'сторона признание', 'высот', 'Голанский']
['Start двигаться', 'сторона признание', 'и']

以下是基于树的方法的说明。颜色表示没有任何分支的部分将被合并;端节点是粗体(那些不必是叶节点)。

enter image description here