有关Python中字符串切片的性能问题

时间:2019-11-08 01:38:38

标签: python-3.x string set slice

我正在学习一些python,在此过程中,我正在从codewars做一些简单的katas。 我遇到了https://www.codewars.com/kata/scramblies问题。

我的解决方法如下:

def scramble(s1,s2):
    result = True
    for character in s2:
        if character not in s1:
            return False
        i = s1.index(character)
        s1 = s1[0:i] + s1[i+1:]
    return result

虽然结果正确,但速度不够快。我的解决方案在12000毫秒后超时。 我查看了其他人提出的解决方案,其中涉及一套解决方案。

  def scramble(s1,s2):
     for letter in set(s2):
        if s1.count(letter) < s2.count(letter):
          return False
     return True

为什么我的解决方案比另一种慢得多?除非我误解了切片字符串的效率,否则它看起来应该不是。我解决这个问题的方法是否有缺陷或不是pythonic?

2 个答案:

答案 0 :(得分:1)

对于这种限制程序运行时间的在线编程挑战,测试输入将包含一些相当大的示例,并且通常设置时间限制,这样您就不必再压缩最后一毫秒的性能了。代码之外,但是您必须编写足够低的computational complexity算法。要回答您的算法超时的原因,我们可以使用big O notation对其进行分析以找到其计算复杂性。

首先,我们可以使用复杂性来标记每个单独的语句,其中ns1的长度,而ms2的长度:

def scramble(s1,s2):
    result = True                  # O(1)
    for character in s2:           # loop runs O(m) times
        if character not in s1:        # O(n) to search characters in s1
            return False               # O(1)
        i = s1.index(character)        # O(n) to search characters in s1
        s1 = s1[0:i] + s1[i+1:]        # O(n) to build a new string
    return result                  # O(1)

则总复杂度为O(1 + m *(n +1 + n + n)+1)或更简单地为O(m * n)。对于这个问题,这不是很有效。


为什么备用算法更快的关键在于set(s2)仅包含字符串s2中的不同字符。这很重要,因为构成这些字符串的字母的大小恒定且有限;小写字母大概为26。鉴于此,替代算法的外循环实际上最多运行26次:

def scramble(s1,s2):
    for letter in set(s2):                      # O(m) to build a set 
                                                # loop runs O(1) times
        if s1.count(letter) < s2.count(letter):     # O(n) + O(m) to count
                                                    # chars from s1 and s2
            return False                            # O(1)
    return True                                 # O(1)

这意味着备用算法的复杂度为O(m + 1 *(n + m + 1)+1)或更简单地为O(m + n),这意味着它比您的算法效率更高。

答案 1 :(得分:0)

首先,set快速且非常擅长于其工作。对于in之类的事物,setlist更快。

第二,您的解决方案比正确的解决方案要做的工作更多。请注意,第二个解决方案是如何从未修改过s1s2的,而您的解决方案都占用了s1两个切片,然后重新分配了s1。这以及调用.index()。切片并不是最快的操作,主要是因为必须分配内存并且必须复制数据。 .remove()可能比.index()和切片的组合要快。

这里的基本信息是,如果任务可以用更少的操作完成,那么显然它将更快地执行。切片也比大多数其他方法昂贵,因为与正确解决方案使用的.count()之类的计算方法相比,分配空间和复制内存是更昂贵的操作。