我有一个大的(50k-100k)字符串mystrings
。 mystrings
中的一些字符串可能是其他字符串的精确子字符串,我想将它们折叠掉(丢弃子字符串并且只保留最长的字符串)。现在我正在使用一种天真的方法,它具有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。
答案 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