Python - 从一个字符串列表中移除任何子串是另一个元素的子串

17

因此,从下面的字符串列表开始:

string_list = ['rest', 'resting', 'look', 'looked', 'it', 'spit']

我想删除任何是另一个元素的子字符串的元素,例如给出以下结果...

string_list = ['resting', 'looked', 'spit']

我有一些代码可以实现这个功能,但它非常丑陋,可能过于复杂。在Python中是否有简单的方法可以做到这一点?


4
让我们来看一下尴尬且丑陋的版本。这会是一个不错的破冰方式。 - mhlester
4
我从未见过有人因为他们的代码在问题中被嘲笑。 - mhlester
我前几天问了一个类似的问题 http://stackoverflow.com/questions/21653585/convert-for-loop-to-list-comprehension-testing-if-elements-in-list-2-are-partia - Darren Haynes
1
可以在这里找到一个类似的问题,可能有更高效的解决方案。 - Aran-Fey
8个回答

14

第一个基本模块:子字符串。

您可以使用 in 来检查:


>>> 'rest' in 'resting'
True
>>> 'sing' in 'resting'
False

接下来,我们将选择创建一个新列表的朴素方法。我们将逐一将项目添加到新列表中,并检查它们是否为子字符串。

def substringSieve(string_list):
    out = []
    for s in string_list:
        if not any([s in r for r in string_list if s != r]):
            out.append(s)
    return out
你可以通过排序来加速,减少比较次数(毕竟,一个更长的字符串永远不可能是一个长度更短/相等的字符串的子串):
def substringSieve(string_list):
    string_list.sort(key=lambda s: len(s), reverse=True)
    out = []
    for s in string_list:
        if not any([s in o for o in out]):
            out.append(s)
    return out

好的,我刚刚修复了它们。我的错。 - Liyan Chang

3
这是一种可能的解决方案:
string_list = ['rest', 'resting', 'look', 'looked', 'it', 'spit']
def string_set(string_list):
    return set(i for i in string_list 
               if not any(i in s for s in string_list if i != s))

print(string_set(string_list))

输出结果为:

set(['looked', 'resting', 'spit'])

请注意我使用生成器表达式创建了一个集合以去除可能重复的单词,因为似乎顺序不重要。


2

另一个一行代码:

[string for string in string_list if len(filter(lambda x: string in x,string_list)) == 1]

应该相当易读,只是不那么像Python。


1
注意Python 3中,filter返回一个迭代器,因此可能会引发TypeError: object of type 'filter' has no len()。只需要用list包装filterlen(list(filter(lambda x: string in x,string_list))) - Hieu
2
另外,如果 string_list 中有重复的哈希值,例如 ['apple', 'apple']。这将返回一个空列表,而不是 ['apple']。这种行为可能是想要的,也可能不是。 - Hieu

0

这里有一种方法:

def find_unique(original):
    output = []

    for a in original:
        for b in original:
            if a == b:
                continue     # So we don't compare a string against itself
            elif a in b:
                break
        else:
            output.append(a) # Executed only if "break" is never hit

    return output

if __name__ == '__main__':
    original = ['rest', 'resting', 'look', 'looked', 'it', 'split']
    print find_unique(original)

它利用了我们可以通过使用in运算符轻松检查一个字符串是否是另一个字符串的子字符串这一事实。它基本上遍历每个字符串,检查它是否是另一个字符串的子字符串,并将自己附加到输出列表中(如果它不是)。

这将打印出 ['resting','looked','split']


0
这里有一个一行代码的解决方案,可以满足你的需求:
filter(lambda x: [x for i in string_list if x in i and x != i] == [], string_list)

例子:

>>> string_list = ['rest', 'resting', 'look', 'looked', 'it', 'spit']
>>> filter(lambda x: [x for i in string_list if x in i and x != i] == [], string_list)
['resting', 'looked', 'spit']

0

这是一种不太优化的方式,仅适用于列表较小的情况:

for str1 in string_list:
    for str2 in string_list:
        if str1 in str2 and str1 != str2:
            string_list.remove(str1)

0
这是一种高效的方法(相对于上面的解决方案;)),因为这种方法大大减少了列表元素之间的比较次数。如果我有一个巨大的列表,我肯定会选择这个方法,当然你也可以将这个解决方案转换成一个lambda函数,使它看起来更简洁:
string_list = ['rest', 'resting', 'look', 'looked', 'it', 'spit']
for item in string_list: 
  for item1 in string_list:
    if item in item1 and item!= item1:
      string_list.remove(item)

print string_list

输出:

>>>['resting', 'looked', 'spit']

希望能有所帮助!

-1

这里还有另一种方法。假设您有一个排序好的列表,并且不必在原地进行筛选,我们只需在一次遍历中选择最长的字符串:

string_list = sorted(string_list)
sieved = []
for i in range(len(string_list) - 1):
    if string_list[i] not in string_list[i+1]:
        sieved.append(string_list[i])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接