从列表中删除子列表

时间:2018-04-09 16:37:05

标签: python list

我想在Python中执行以下操作:

A = [1, 2, 3, 4, 5, 6, 7, 7, 7]
C = A - [3, 4]  # Should be [1, 2, 5, 6, 7, 7, 7]
C = A - [4, 3]  # Should not be removing anything, because sequence 4, 3 is not found

所以,我只是想从另一个列表中删除子列表(作为序列)的第一个外观。我怎么能这样做?

编辑:我说的是列表,而不是集合。这意味着项目的排序(序列)很重要(A和B中的 ),以及重复项。

4 个答案:

答案 0 :(得分:3)

我认为这个问题就像一个子字符串搜索,因此可以在此处应用 KMP BM sub-string search algorithm。即使您希望支持多种模式,也有一些模式算法,例如 Aho-Corasick Wu-Manber 等。

以下是来自GitHub Gist的KMP algorithm implemented by Python PS:作者不是我。我只是想分享我的想法。

class KMP:
    def partial(self, pattern):
        """ Calculate partial match table: String -> [Int]"""
        ret = [0]

        for i in range(1, len(pattern)):
            j = ret[i - 1]
            while j > 0 and pattern[j] != pattern[i]:
                j = ret[j - 1]
            ret.append(j + 1 if pattern[j] == pattern[i] else j)
        return ret

    def search(self, T, P):
        """
        KMP search main algorithm: String -> String -> [Int]
        Return all the matching position of pattern string P in S
        """
        partial, ret, j = self.partial(P), [], 0

        for i in range(len(T)):
            while j > 0 and T[i] != P[j]:
                j = partial[j - 1]
            if T[i] == P[j]: j += 1
            if j == len(P):
                ret.append(i - (j - 1))
                j = 0

        return ret

然后用它来计算匹配的位置,最后删除匹配:

A = [1, 2, 3, 4, 5, 6, 7, 7, 7, 3, 4]
B = [3, 4]
result = KMP().search(A, B)
print(result)
#assuming at least one match is found
print(A[:result[0]:] + A[result[0]+len(B):])

<强>输出:

[2, 9]
[1, 2, 5, 6, 7, 7, 7, 3, 4]
[Finished in 0.201s]

PS :您也可以尝试其他算法。 @Pault的答案非常好,除非你非常关心表演。

答案 1 :(得分:2)

如果你想删除确切的序列,可以采用以下方法:

通过检查子列表是否与所需序列匹配来查找坏索引:

bad_ind = [range(i,i+len(B)) for i,x in enumerate(A) if A[i:i+len(B)] == B]
print(bad_ind)
#[[2, 3]]

因为这会返回一个列表列表,将其展平并将其转换为集合:

bad_ind_set = set([item for sublist in bad_ind for item in sublist])
print(bad_ind_set)
#set([2, 3])

现在使用此集按索引过滤原始列表:

C = [x for i,x in enumerate(A) if i not in bad_ind_set]
print(C)
#[1, 2, 5, 6, 7, 7, 7]

以上bad_ind_set将删除序列的所有匹配项。如果你只想删除第一场比赛,那就更简单了。您只需要bad_ind的第一个元素(无需展平列表):

bad_ind_set = set(bad_ind[0])

更新:这是一种使用短路for循环查找和删除第一个匹配子序列的方法。这会更快,因为一旦找到第一个匹配就会爆发。

start_ind = None
for i in range(len(A)):
    if A[i:i+len(B)] == B:
        start_ind = i
        break

C = [x for i, x in enumerate(A) 
     if start_ind is None or not(start_ind <= i < (start_ind + len(B)))]
print(C)
#[1, 2, 5, 6, 7, 7, 7]

答案 2 :(得分:0)

使用套装:

C = list(set(A) - set(B))

如果你想复制重复:

filter_set = set(B)
C = [x for x in A if x not in filter_set]

答案 3 :(得分:0)

这是另一种方法:

# Returns that starting and ending point (index) of the sublist, if it exists, otherwise 'None'.

def findSublist(subList, inList):
    subListLength = len(subList)
    for i in range(len(inList)-subListLength):
        if subList == inList[i:i+subListLength]:
            return (i, i+subListLength)
    return None


# Removes the sublist, if it exists and returns a new list, otherwise returns the old list.

def removeSublistFromList(subList, inList):
    indices = findSublist(subList, inList)
    if not indices is None:
        return inList[0:indices[0]] + inList[indices[1]:]
    else:
        return inList


A = [1, 2, 3, 4, 5, 6, 7, 7, 7]

s1 = [3,4]
B = removeSublistFromList(s1, A)
print(B)

s2 = [4,3]
C = removeSublistFromList(s1, A)
print(C)