2016-09-13 65 views
0

我最近使用dictoionaries编写了一个python解决方案,它得到了TLE判决。该解决方案与C++中的multiset解决方案非常相似。所以,我们确信这个逻辑是正确的,但是实现并不符合标准。如何提高python字典性能?

问题描述为下面的代码的理解(http://codeforces.com/contest/714/problem/C):

  • 对于每一个我们需要得到0和1组成的串号,使得第i个数字是0/1,如果在数量上相应的第i位是偶数。
  • 我们需要维护具有上述点给出的相同映射的数量。

任何提示/指针,以改善以下代码的性能?它为一个大型测试案例(http://codeforces.com/contest/714/submission/20594344)提供了TLE(超出时间限制)。

from collections import defaultdict 

def getPattern(s): 
    return ''.join(list(s.zfill(19))) 

def getSPattern(s): 
    news = s.zfill(19) 
    patlist = [ '0' if (int(news[i])%2 == 0) else '1' for i in range(19) ] 
    return "".join(patlist) 


t = int(raw_input()) 
pat = defaultdict(str) # holds strings as keys and int as value 

for i in range(0, t): 
    oper, num = raw_input().strip().split(' ') 

    if oper == '+' : 
     pattern = getSPattern(str(num)) 
     if pattern in pat: 
      pat[pattern] += 1 
     else: 
      pat[pattern] = 1 
    elif oper == '-' : 
     pattern = getSPattern(str(num)) 
     pat[pattern] = max(pat[pattern] - 1, 0) 
    elif oper == '?' : 
     print pat.get(getPattern(num) , 0) 
+0

尽管不是perf-tuning方面的专家,但我期望字典查找性能相当高。我会倾向于更多地关注'getSPattern'函数,因为我相信可以从中挤出一些东西。现在,在我们开始之前,我阅读了比赛,但是无法得到时间限制的衡量标准:它只是在'? '测试? – sal

+0

@sal时间限制是根据每个测试用例执行测量的。因此,对于输入数字为100000的TLE的大型测试用例。如果您滚动到提交链接的最底部,则可以检查该内容。 – mtk

+0

明白了。试试这个版本:https://eval.in/641639我只改变了你的'getSPattern',并删除了'defaultdict'(尽管你可以保留它)。看看这是否能够提升你的表现。如果是的话,我会添加一个答案,提供更多关于它的细节。 – sal

回答

2

我看到很多的小问题与您的代码,但不能说,如果他们加起来显著的性能问题:

您已经成立,并使用您的defaultdict()错误:

pat = defaultdict(str) 
... 
if pattern in pat: 
    pat[pattern] += 1 
else: 
    pat[pattern] = 1 

构造函数defaultdict()的参数应该是值的类型,而不是键的类型。一旦你设置了defaultdict正确,你可以简单地做:

pat = defaultdict(int) 
... 
pat[pattern] += 1 

当值现在的默认值为零,如果该模式是不存在的话。

由于规范说:

- 爱 - 删除非负整数的单个发生从多重集的AI。这是保证,至少有一个ai在 multiset。

那么这个:

pat[pattern] = max(pat[pattern] - 1, 0) 

可以简单地是这样的:

pat[pattern] -= 1 

你有19个字符串的工作,但由于该规范说的数字将低于10 ** 18,你可以用18个字符串来代替。

getSPattern()做了zfill(),然后处理字符串,它应该做它以相反的顺序,过程中的字符串,然后zfill()它,因为没有必要的前导零运行的逻辑。

我们不需要的int()开销将字符转换为数字:

(int(news[i])%2 == 0) 

考虑使用ord()而不是作为数字的ASCII值具有相同的奇偶数字本身:ord('4') - > 52

而且你不需要遍历索引,你可以简单地遍历字符。

下面是我用上面的修改代码的返工,看它是否仍然有效并获得您的任何性能(!):

from collections import defaultdict 

def getPattern(string): 
    return string.zfill(18) 

def getSPattern(string): 
    # pattern_list = (('0', '1')[ord(character) % 2] for character in string) 
    pattern_list = ('0' if ord(character) % 2 == 0 else '1' for character in string) 
    return ("".join(pattern_list)).zfill(18) 

patterns = defaultdict(int) # holds keys as strings as and values as int 

text = int(raw_input()) 

for _ in range(text): 
    operation, number = raw_input().strip().split() 

    if operation == '+': 
     pattern = getSPattern(number) 
     patterns[pattern] += 1 
    elif operation == '-': 
     pattern = getSPattern(number) 
     patterns[pattern] -= 1 
    elif operation == '?': 
     print patterns.get(getPattern(number), 0) 
+0

感谢您的精彩投入,惊讶于我的代码中看到这么多的缺陷:)。你的版本被接受http://codeforces.com/contest/714/submission/20615068 – mtk

2

已同意@cdlane做了解释,我只需要加上我的重写getSPattern,我认为大部分时间都花在这里。按我最初的注释,这是可在https://eval.in/641639

def getSPattern(s): 
    patlist = ['0' if c in ['0', '2', '4', '6', '8'] else '1' for c in s] 
    return "".join(patlist).zfill(19) 

使用zfill(18)可能会稍微爱惜你一段时间。

+0

我会用'patstr =''.join(chr(0x30 + ord(c)&1)for c)s' –

+0

@ MarkRansom很酷,我会尝试一下,但不需要使用十六进制:'[chr(48+(ord(c)&1))for c in s]' – sal