我最近试图在Hackerrank上解决this problem。我最终得到了以下解决方案,在给定的时间限制内给出了正确的答案:
from collections import Counter
lisa=[]
for each in range(input()):
current=raw_input()
count=0
lookup_dict={}
i=0
for i in range(len(current)):
for j in range(i,len(current)):
sub_string=current[i:j+1]
sub_string=''.join(sorted(sub_string))
if sub_string in lookup_dict.keys():
lookup_dict[sub_string]+=1
else:
lookup_dict[sub_string]=1
for k in lookup_dict.values():
count+=k*(k-1)/2
print count
我的问题是他们提供的解决方案(下面再现)明显快于我写的解决方案,即使使用的复杂性和方法是相同的。
from collections import *
for i in range(input()):
s = raw_input()
check = defaultdict(int)
l = len(s)
for i in range(l):
for j in range(i+1,l+1):
sub = list(s[i:j])
sub.sort()
sub = "".join(sub)
check[sub]+=1
sum = 0
for i in check:
n = check[i]
sum += (n*(n-1))/2
print sum
我的猜测是它与defaultdict
方法有关,但无法找出原因?
答案 0 :(得分:4)
在Python 2.x中,dict.keys()
返回一个列表,在您的第一个解决方案中,您实际上正在执行 -
if sub_string in lookup_dict.keys()
这将是一个O(n)操作(n是dictonary的大小 - lookup_dict
),因为.keys()
实际返回一个列表,并且包含检查列表是O(n),也是最有可能是必须创建列表的额外成本。
而在第二种方法中,您没有任何此类检查,而defaultdict正在处理为您自动设置默认值,这可能是您的第一个解决方案明显慢于第二个解决方案的原因之一(鉴于此你在最里面的循环中进行dict.keys()
查找,以便多次查找。
显示dict.keys()
返回列表的示例 -
>>> d = {1:2,3:4,5:6,7:8}
>>> d.keys()
[1, 3, 5, 7]
答案 1 :(得分:1)
谈论defaultdict
:与普通密钥检查相比,它的优化程度。即:
x = defaultdict(int)
for i in xrange(...):
x[i] += 1
比
快〜50%x = {}
for i in xrange(...):
if i in x:
x[i] +=1
else:
x[1] = 1
如果缺少所有键的情况。
但主要的情况是在py2中调用dict.keys()
实际上会创建一个列表。因此,检查key in dict.keys()
需要首先为列表分配内存,然后使用实际键值填充它,然后检查您的密钥。最糟糕的是,在此列表应该由垃圾收集器清理之后,在下一个for
步骤中,您将执行相同的操作,除了将为该列表分配更多内存。所以,在你的例子中,这实际上会扼杀性能。