在一组字符串

时间:2017-01-19 07:48:24

标签: python string set substring

我有一个大的(50k-100k)字符串mystringsmystrings中的一些字符串可能是其他字符串的精确子字符串,我想将它们折叠掉(丢弃子字符串并且只保留最长的字符串)。现在我正在使用一种天真的方法,它具有O(N^2)复杂度。

unique_strings = set()
for s in sorted(mystrings, key=len, reverse=True):
    keep = True
    for us in unique_strings:
        if s in us:
            keep = False
            break
    if keep:
        unique_strings.add(s)

哪种数据结构或算法可以简化此任务,而不需要O(N^2)次操作。图书馆还可以,但我需要保持纯Python。

4 个答案:

答案 0 :(得分:1)

在set()中查找子字符串:

name = set()
name.add('Victoria Stuart')                         ## add single element
name.update(('Carmine Wilson', 'Jazz', 'Georgio'))  ## add multiple elements
name
{'Jazz', 'Georgio', 'Carmine Wilson', 'Victoria Stuart'}

me = 'Victoria'
if str(name).find(me):
    print('{} in {}'.format(me, name))
# Victoria in {'Jazz', 'Georgio', 'Carmine Wilson', 'Victoria Stuart'}

这很简单-如果要返回匹配的字符串,则有些问题:

for item in name:
    if item.find(me):
            print(item)
'''
Jazz
Georgio
Carmine Wilson
'''

print(str(name).find(me))
# 39    ## character offset for match (i.e., not a string)

如您所见,上面的循环仅执行直到条件为True,并在打印所需的项目(匹配字符串)之前终止。

使用正则表达式(正则表达式)可能更好,更容易:

import re

for item in name:
    if re.match(me, item):
            full_name = item
            print(item)
# Victoria Stuart
print(full_name)
# Victoria Stuart

for item in name:
    if re.search(me, item):
            print(item)
# Victoria Stuart

来自Python docs

搜索()vs匹配()

Python提供了两种基于常规的不同原始操作 表达式:re.match()仅在开始时检查匹配项 字符串,而re.search()检查字符串中是否有匹配项 字符串...

答案 1 :(得分:0)

一种天真的方法:

1. sort strings by length, longest first  # `O(N*log_N)`
2. foreach string:  # O(N)
    3. insert each suffix into tree structure: first letter -> root, and so on.  
       # O(L) or O(L^2) depending on string slice implementation, L: string length
    4. if inserting the entire string (the longest suffix) creates a new 
       leaf node, keep it!

O[N*(log_N + L)]  or  O[N*(log_N + L^2)]

这可能远非最优,但对于大O(N^2)(字符串数)和小N(平均字符串长度),应该明显优于L

您还可以按长度按降序迭代字符串,并将每个字符串的所有子字符串添加到一个集合中,并且只保留那些不在集合中的字符串。算法大O应该与上面更糟糕的情况(O[N*(log_N + L^2)])相同,但实现更简单:

seen_strings, keep_strings = set(), set()
for s in sorted(mystrings, key=len, reverse=True):
    if s not in seen_strings:
        keep_strings.add(s)
        l = len(s)
        for start in range(0, l-1):
            for end in range(start+1, l):
                seen_strings.add(s[start:end])

答案 2 :(得分:0)

与此同时,我提出了这种方法。

      mydata <- Hitters[sample(nrow(Hitters)),]

      folds <- cut(seq(1, nrow(mydata)), breaks = 10, labels = F)

      for (i in 1:10) {

            testindex <- which(folds == i)
            testdata <- mydata[testindex,]
            traindata <- mydata[ - testindex,]

  #there may be an error after this


            best.fits11 <- regsubsets(Salary ~ ., data = traindata, nvmax=19)
            test[i]<-coef(best.fits11, id = 19) * testdata
            train[i]<-traindata
            }
    for (i in 1:10) { 
    err.rss <-mean((test[i]-train[i])^2 )
    }

:≈15分钟 - &gt;我正在使用的数据集≈20秒。 错误:引入from Bio.trie import trie unique_strings = set() suffix_tree = trie() for s in sorted(mystrings, key=len, reverse=True): if suffix_tree.with_prefix(contig) == []: unique_strings.add(s) for i in range(len(s)): suffix_tree[s[i:]] = 1 作为依赖项,既不是轻量级也不是纯Python(正如我最初提出的那样)。

答案 3 :(得分:0)

您可以对字符串进行预排序,并创建一个字典,该字典将字符串映射到排序列表中的位置。然后,您可以遍历字符串(O(N))和后缀(O(L))的列表,并将位置dict(O(1)dict查找和O( 1)列表更新)。因此总的来说,这具有O(N * L)的复杂度,其中None是平均字符串长度。

L

对以下示例数据进行测试得出的加速因子约为21:

strings = sorted(mystrings, key=len, reverse=True)
index_map = {s: i for i, s in enumerate(strings)}
unique = set()
for i, s in enumerate(strings):
    if s is None:
        continue
    unique.add(s)
    for k in range(1, len(s)):
        try:
            index = index_map[s[k:]]
        except KeyError:
            pass
        else:
            if strings[index] is None:
                break
            strings[index] = None