【问题标题】:Python check for Anagram in O(n) solutionPython 检查 O(n) 解决方案中的 Anagram
【发布时间】:2016-03-14 02:33:45
【问题描述】:

我正在尝试检查 2 个字符串是否是字谜。这个解决方案很简单,但效率不高(Ologn)我知道我可以使用 Collections 和 Counter,然后比较每个字符的出现,但我试图避免使用任何模块进行面试。解决这个问题的最快方法是什么? (也许,检查每个字符的出现?)

def check(word1,word2):

    return sorted(word1)==sorted(word2)

【问题讨论】:

  • 快速甚至不是这里的问题。您的代码中有多个错误,使其认为“汽车”和“猫”是字谜。
  • if sorted(l1) == sorted(l2) 是,取决于sort 是怎么做的,大概是O(nlogn)
  • @user2357112 大坝真的。为什么要这样做。对不起,我是新人。
  • @RNar 你能解释一下“依赖”是什么意思吗? O(nlogn) 比 O(n) 快吗?
  • 这种 O(nlogn) 是您遇到的最少的问题。如果您只比较 2 个单词 - 谁在乎?如果你问我,在单词集合中查找字谜的 O(n*n) 是一个更有趣的问题

标签: python


【解决方案1】:

您的代码甚至没有返回正确的值。这个单行是 O(n log n):

return sorted(word1) == sorted(word2)

对于 O(n) 解决方案,您可以计算所有字符:

from collections import Counter
# ...
def check(a, b)
  return Counter(a) == Counter(b)

如果没有集合,它会更长:

def check(a, b):
    chars = dict.fromkeys(a + b, 0)
    for c in a:
        chars[c] += 1
    for c in b:
        chars[c] -= 1
    return not any(chars.values())

此代码执行以下操作:

  • chars = dict.fromkeys(a + b, 0):创建一个字典,将任一单词中出现的所有字符作为键设置为 0。
  • for c in a: chars[c] += 1:这将遍历 a 并计算其中每个字符的出现次数。 chars 现在包含单独字符的计数,(以及 b 中的字符的一些零,但不包含 a)
  • for c in b: chars[c] -= 1:和以前一样,但是这会从chars 中减去b 的字符数
  • return not any(chars.values()): chars['h'] == 0 当且仅当 ab 具有相同数量的 'h'。此行检查 chars 是否只有零作为值,这意味着所有字符在两个输入中的计数相同。 (any 返回序列中是否有真值。0 为假,其他整数为真。)

两个列表都被迭代一次。假设字典的 O(1) 访问时间使整个算法在 O(n) 时间内运行(其中 n 是输入的总长度)。空间复杂度也是 O(n)(所有字符都可以不同)。当他们问你复杂性时,不要犯这个错误。这不是必要的时间复杂度。

【讨论】:

  • 从集合导入计数器
  • 是的,我的错。不过我不想使用集合,我认为面试官不会喜欢它。第一个解决方案不是最快的吧?
  • @Angular:在内部,python 使用 O(n log n) 算法进行排序,所以是的,第一个算法越来越慢。
  • @Angular 我不认识所有的面试官,但如果我是其中之一,我希望人们知道他们正在使用的平台。编写好的算法是一项重要的技能。了解您使用的平台的优势是另一回事。
  • 你可以通过初始化chars = dict.fromkeys(a + b, 0)来简化
【解决方案2】:

这是http://interactivepython.org/runestone/static/pythonds/AlgorithmAnalysis/AnAnagramDetectionExample.html 的一个不错的选择:

def anagramSolution(s1,s2):

    TABLE_SIZE = 128
    c1 = [0]*TABLE_SIZE
    c2 = [0]*TABLE_SIZE

    for ch in s1:
        pos = ord(ch)
        c1[pos] = c1[pos] + 1

    for ch in s2:
        pos = ord(ch)
        c2[pos] = c2[pos] + 1

    j = 0
    stillOK = True
    while j<TABLE_SIZE and stillOK:
        if c1[j]==c2[j]:
            j = j + 1
        else:
            stillOK = False

    return stillOK

这在 O(n) 中运行。本质上,您循环遍历两个字符串,计算每个字母的出现次数。最后,您可以简单地遍历每个字母,确保计数相等。

正如 cmets 中所指出的,这对于 unicode 的扩展将更加困难。如果您希望使用 unicode,则可能需要使用字典。

【讨论】:

  • 我认为这个答案很有价值,因为它是不导入作业的解决方案。但是,一旦stillOKFalse,则无需继续循环。此时您可以简单地返回False
  • 确实如此。一旦 stillOk 设置为 False,循环将终止。见while j&lt;26 and stillOK:
  • 所以 ord 是一个内置函数,它返回 ASCII 字符或每个字母之类的?
  • 没错。这提出了一个很好的观点,这假设字符串是小写的。如果它们是大写的,它将无法按预期工作。您可以使数组大一倍以考虑大写字母,从每个中减去 ord('A')。你也可以使用字典,只存储你遇到的字符。最后,您将不得不遍历键。
  • 即使是大写字母也需要修改
【解决方案3】:

如果没有导入,我会这样写:

def count_occurences(mystring):
    occs = {}
    for char in mystring:
        if char in occs:
            occs[char] += 1
        else:
            occs[char] = 1
    return occs

def is_anagram(str1, str2):
    return count_occurences(str1) == count_occurences(str2)

或者,如果您可以使用导入,而不是 Counter,请使用 defaultdict

from collections import defaultdict

def count_occurences(mystring):
    occs = defaultdict(int)
    for char in mystring:
        occs[char] += 1

    return occs

def is_anagram(str1, str2):
    return count_occurences(str1) == count_occurences(str2)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-19
    • 2018-02-08
    • 2020-03-02
    • 2012-02-10
    相关资源
    最近更新 更多