如何压缩此字符串压缩代码以使其更有效?

时间:2019-01-12 04:05:39

标签: python arrays data-structures hashtable

我刚刚编写了针对问题1.6字符串压缩的代码,该代码来自破解编码面试。我想知道如何压缩此代码以使其更有效。另外,我想确保此代码为O(n),因为我没有连接到新字符串。

问题指出:

  

实现一种使用重复字符计数执行基本字符串压缩的方法。例如,字符串'aabcccccaaa'将变为a2b1c5a3。如果“压缩”字符串不会变得小于原始字符串,则您的方法应返回原始字符串。您可以假设该字符串仅包含大写和小写字母(a-z)。

我的代码有效。我在if之后的第一个else语句检查该字符的计数是否为1,以及是否仅追加该字符。我这样做是为了检查最终结果的长度和原始字符串以确定返回哪个字符串。

import string


def stringcompress(str1):
    res = []
    d = dict.fromkeys(string.ascii_letters, 0)
    main = str1[0]
    for char in range(len(str1)):
        if str1[char] == main:
            d[main] += 1
        else:
            if d[main] == 1:
                res.append(main)
                d[main] = 0
                main = str1[char]
                d[main] += 1
            else:
                res.append(main + str(d[main]))
                d[main] = 0
                main = str1[char]
                d[main] += 1

    res.append(main + str(d[main]))
    return min(''.join(res), str1)

同样,我的代码可以按预期工作,并且可以执行问题所要求的操作。我只想看看是否有某些代码行可以删除,以使程序更高效。

1 个答案:

答案 0 :(得分:1)

我搞砸了用timeit模块测试不同的版本。当我生成不经常重复的测试数据时,您的变体效果非常好,但是对于短字符串,我的stringcompress_using_string是最快的方法。随着琴弦的变长,一切都将倒过来,您的处事方法将变得最快,而stringcompress_using_string则将变得最慢。

这只是显示了在不同情况下进行测试的重要性。我最初的结论不完整,并拥有更多的测试数据,这表明了这三种方法的有效性。

import string
import timeit
import random

def stringcompress_original(str1):
    res = []
    d = dict.fromkeys(string.ascii_letters, 0)
    main = str1[0]
    for char in range(len(str1)):
        if str1[char] == main:
            d[main] += 1
        else:
            if d[main] == 1:
                res.append(main)
                d[main] = 0
                main = str1[char]
                d[main] += 1
            else:
                res.append(main + str(d[main]))
                d[main] = 0
                main = str1[char]
                d[main] += 1

    res.append(main + str(d[main]))
    return min(''.join(res), str1, key=len)

def stringcompress_using_list(str1):
    res = []

    count = 0
    for i in range(1, len(str1)):
        count += 1

        if str1[i] is str1[i-1]:
            continue

        res.append(str1[i-1])
        res.append(str(count))
        count = 0
    res.append(str1[i] + str(count+1))

    return min(''.join(res), str1, key=len)

def stringcompress_using_string(str1):
    res = ''

    count = 0
    # we can start at 1 because we already know the first letter is not a repition of any previous letters
    for i in range(1, len(str1)):
        count += 1

        # we keep going through the for loop, until a character does not repeat with the previous one
        if str1[i] is str1[i-1]:
            continue

        # add the character along with the number of times it repeated to the final string
        # reset the count
        # and we start all over with the next character
        res += str1[i-1] + str(count)
        count = 0
    # add the final character + count
    res += str1[i] + str(count+1)

    return min(res, str1, key=len)

def generate_test_data(min_length=3, max_length=300, iterations=3000, repeat_chance=.66):
    assert repeat_chance > 0 and repeat_chance < 1
    data = []
    chr = 'a'
    for i in range(iterations):
        the_str = ''
        # create a random string with a random length between min_length and max_length
        for j in range( random.randrange(min_length, max_length+1) ):
            # if we've decided to not repeat by randomization, then grab a new character,
            # otherwise we will continue to use (repeat) the character that was chosen last time
            if random.random() > repeat_chance:
                chr = random.choice(string.ascii_letters)
            the_str += chr
        data.append(the_str)
    return data


# generate test data beforehand to make sure all of our tests use the same test data
test_data = generate_test_data()

#make sure all of our test functions are doing the algorithm correctly
print('showing that the algorithms all produce the correct output')
print('stringcompress_original: ', stringcompress_original('aabcccccaaa'))
print('stringcompress_using_list: ', stringcompress_using_list('aabcccccaaa'))
print('stringcompress_using_string: ', stringcompress_using_string('aabcccccaaa'))
print()

print('stringcompress_original took', timeit.timeit("[stringcompress_original(x) for x in test_data]", number=10, globals=globals()), ' seconds' )
print('stringcompress_using_list took', timeit.timeit("[stringcompress_using_list(x) for x in test_data]", number=10, globals=globals()), ' seconds' )
print('stringcompress_using_string took', timeit.timeit("[stringcompress_using_string(x) for x in test_data]", number=10, globals=globals()), ' seconds' )

以下结果全部取自2.70GHz @四核Intel i7-5700HQ CPU。比较每个块引用中的不同功能,但不要尝试将一个块引用的结果与另一个块引用进行交叉比较,因为测试数据的大小会有所不同。


使用长字符串

使用generate_test_data(10000, 50000, 100, .66)

生成的测试数据
  

stringcompress_original用了7.346990528497378秒
  stringcompress_using_list用了7.589927956366313秒
  stringcompress_using_string用了7.713812443264496秒


使用短字符串

使用generate_test_data(2, 5, 10000, .66)

生成的测试数据
  

stringcompress_original用了0.​​40272931026355685秒
  stringcompress_using_list花费了0.1525574881739265秒
  stringcompress_using_string花了0.13842854253813164秒


重复字符的机会为10%

使用generate_test_data(10, 300, 10000, .10)

生成的测试数据
  

stringcompress_original用了4.675965586924492秒
  stringcompress_using_list用了6.081609410376534秒
  stringcompress_using_string花费了5.887430301813865秒


有90%的机会重复字符

使用generate_test_data(10, 300, 10000, .90)

生成的测试数据
  

stringcompress_original花了2.6049783549783547秒
  stringcompress_using_list用了1.9739111725413099秒
  stringcompress_using_string花费了1.9460854974553605秒


创建一个像这样的小框架很重要,可用于测试算法的更改。通常,似乎没有用的更改会使您的代码运行得更快,因此优化性能时游戏的关键是尝试不同的事情,并计时结果。我敢肯定,如果您尝试进行不同的更改,还会发现更多发现,但这对您要优化的数据类型确实很重要-压缩短字符串vs长字符串vs不重复的字符串与经常这样做的人一样。