Python 是否可以在不使用sorted()或比O(n^2)性能更好的字典的情况下检查字谜?
我目前正在学习,有人告诉我不要在python中使用“魔法”python技巧,如sorted()或字符串拆分 我误认为这只是使用数组来检查输入字符串是否是字谜。因此,虽然我想到的第一件事是使用字典,但我没有继续使用它,因为我认为它是被禁止的。使用字典,我可以通过使用字母作为键,计数作为值,向下计数(减去)输入字符串中遇到的字母频率,并对频率字典进行循环以查看是否有零来计算字母的频率 所以…由于对限制设置了错误的概念,我创建了一个嵌套循环,如下所示(问题假设长度相等,没有空间) 显然,这是不可取的,因为对于使用字典的解决方案,大o表示法是o(n^2),即o(3n),两次迭代计算频率,最后一次迭代检查字典中的任何条目是否具有非零频率(这意味着这不是一个字谜) 因此,这是我的理解问题,但我没有继续,而是在想,有没有可能在不使用字典和仅依赖数组/列表的情况下产生一个比我的解决方案O(n^2)更好的性能更好的字谜检查器 我有另一个想法,但我停止了: 1) 将字符串列表转换为数字列表-但这意味着我仍然需要循环参考字符(原始)以查找数字位置Python 是否可以在不使用sorted()或比O(n^2)性能更好的字典的情况下检查字谜?,python,algorithm,Python,Algorithm,我目前正在学习,有人告诉我不要在python中使用“魔法”python技巧,如sorted()或字符串拆分 我误认为这只是使用数组来检查输入字符串是否是字谜。因此,虽然我想到的第一件事是使用字典,但我没有继续使用它,因为我认为它是被禁止的。使用字典,我可以通过使用字母作为键,计数作为值,向下计数(减去)输入字符串中遇到的字母频率,并对频率字典进行循环以查看是否有零来计算字母的频率 所以…由于对限制设置了错误的概念,我创建了一个嵌套循环,如下所示(问题假设长度相等,没有空间) 显然,这是不可取的,
它一直在蚕食着我,我意识到我对这些简单的算法问题想得太多了……但我仍然好奇是否有一个解决方案符合标准。这取决于您输入的定义。是要处理所有字符,还是只处理可打印/拉丁-1集?你关心理论上的复杂性,还是真正的性能 对于第一个问题-如果您不关心字符编码到多个字节,您可以创建一个包含256个元素的列表,而不是索引到dict中,而是索引该数组。对于每个字符,在列表中的特定位置添加/删除
1
。它的复杂性与dict解决方案相同:O(n+m)
。(数组中的计数是O(1)
,因为它具有预定义的大小)
对于第二个问题-如果您想不受限制地使用字符,您可以做同样的事情,但是创建一个包含
1114112
元素的列表,并按unicode字符编号进行索引。它不会比字典解决方案快,但同样-复杂性仍然是O(n+m)
回答这个问题的python方法是使用集合。Counter
对象:
from collections import Counter
def anagram(s1, s2):
return Counter(s1) == Counter(s2)
但由于这些都受到限制,您可以使用普通字典(也称为hashmaps,它是许多高效算法的基本组成部分)
一个高层次的过程如下。首先,为
string1
构建计数的哈希映射。对string2
重复此过程。最后,比较两个哈希映射是否相等
首先,辅助函数-
def build_counts(string):
ctr = {}
for c in string:
ctr[c] = ctr.setdefault(c, 0) + 1
return ctr
现在,司机-
def anagram(string1, string2):
c1 = build_counts(string1)
c2 = build_counts(string2)
return c1 == c2
复杂性分析-构建每个hashmap需要O(N)个时间,执行比较也需要O(N),因为您必须,一个测试键是否相同,另一个测试键是否相同,比较相应键的值。总之,一个线性算法
由于散列映射和散列集非常常见,您不应该认为这是有限制的,除非您计划使用数组和开放寻址实现自己的散列映射 不,没有一种高效的算法不依赖于哈希映射或更复杂的东西。除非您使用viraptor的答案,它基本上是hashmap(!)的数组版本,但是ASCII集中的每个字符都有一个唯一的条目。例如,ASCII字符65的计数将通过
arr[65]
访问,依此类推。因此,您需要有一个足够大的数组来容纳每个ASCII字符
只要ASCII字母是可以管理的,但是当你考虑其他更广泛的编码(Unicode)时,情节就会变得更复杂。最后,只使用hashmap会节省很多空间 如果不允许使用魔法功能,您可以构建自己的计数器
def计数器(字符串):
返回{i:string.count(i)for i in set(string)}
然后您可以简单地使用cᴏʟᴅsᴘᴇᴇᴅ's解决方案这里有一种替代方法,可以在线性时间内处理“合理”长度的单词。如果不计算任意精度乘法,则该算法运行O(n) 逻辑,如果你把每个字母都分配给一个素数。两个字谜的这些素数的乘法是相同的 我希望reduce不算是一个神奇的函数
from operator import mul
from functools import reduce
def is_anagram(word_a, word_b):
primes_26 = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,
41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
def prime_product(word):
return reduce(mul, [primes_26[ord(ch) - 65] for ch in word.upper() if ch.isalpha()])
return prime_product(word_a) == prime_product(word_b)
assert is_anagram("abc", "cba")
assert not is_anagram("abc", "cbad")
通过比较所有26个字母并计数,得出T(n)=2n+26
O(n)
def anagram解决方案4(s1、s2):
c1=[0]*26
c2=[0]*26
对于范围内的i(len(s1)):
pos=ord(s1[i])-ord('a')
c1[pos]=c1[pos]+1
对于范围内的i(len(s2)):
pos=ord(s2[i])-ord('a'))
c2[pos]=c2[pos]+1
j=0
stillOK=True
而你可以用一个Counter(original)=Counter(input)
应该相当于一个字谜检查。@PatrickHaugh这很好,但在这种情况下,这样的神奇功能受到限制。Counter
实际上是一个dict,所以如果排除dict,我假设计数器也被排除。如果我要这样做,而不使用任何类型的映射类型,我会使用数组按其0-25偏移量(或0-51,如果认为大写字母不同)索引每个字母,并将计数构建到其中。如果这是为了研究,那么它可能值得精确-您提供的函数是O(n*m)
,而您使用的字典解决方案是
from operator import mul
from functools import reduce
def is_anagram(word_a, word_b):
primes_26 = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,
41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
def prime_product(word):
return reduce(mul, [primes_26[ord(ch) - 65] for ch in word.upper() if ch.isalpha()])
return prime_product(word_a) == prime_product(word_b)
assert is_anagram("abc", "cba")
assert not is_anagram("abc", "cbad")
def check_anagram(data1,data2):
flag = False
if (len(data1)==len(data2)):
if (set(data1.lower()) == set(data2.lower())):
for i in range(len(data1)-1):
if data1[i] != data2[i]:
flag = True
return flag
print(check_anagram("Theclassroom","Schoolmaster"))