检查两个字符串是否是Python中彼此的排列

时间:2008-12-28 17:25:18

标签: algorithm python

我正在检查两个字符串ab是否是彼此的排列,我想知道在Python中执行此操作的理想方法是什么。从Python的禅宗,“应该有一个 - 最好只有一个 - 显而易见的方式”,但我发现至少有两种方式:

sorted(a) == sorted(b)

all(a.count(char) == b.count(char) for char in a)

但第一个更慢(例如)a的第一个字符在b中没有,而第二个字符在实际排列时更慢。

有更好的方法(无论是更多的Pythonic,还是平均更快的意义上)的方式呢?或者我应该从这两个中选择,具体取决于我期望最常见的情况?

22 个答案:

答案 0 :(得分:19)

这是一种O(n)的方法,渐渐地比你建议的两种方式更好。

import collections

def same_permutation(a, b):
    d = collections.defaultdict(int)
    for x in a:
        d[x] += 1
    for x in b:
        d[x] -= 1
    return not any(d.itervalues())

## same_permutation([1,2,3],[2,3,1])
#. True

## same_permutation([1,2,3],[2,3,1,1])
#. False

答案 1 :(得分:17)

“但是当(例如)a的第一个字符在b中无处时,第一个更慢。”

这种退化案例的性能分析并不是一个好主意。想到各种不起眼的特殊情况,这是一个失去时间的老鼠洞。

只进行 O 式“整体”分析。

总体而言,排序 O n 日志( n ))。

a.count(char) for char in a解决方案 O n 2 )。每次计数都是对字符串的全面检查。

如果一些不起眼的特殊情况碰巧更快 - 或更慢,那可能很有趣。但只有当你知道你不明显的特殊情况的频率时才重要。在分析排序算法时,重要的是要注意,相当数量的排序涉及的数据已经按照正确的顺序(通过运气或巧妙的设计),因此对预排序数据的排序性能很重要。

在你不起眼的特殊情况下(“a的第一个字母在b中无处可去”)这是否经常发生?如果它只是你想到的特殊情况,请把它放在一边。如果这是关于您的数据的事实,那么请考虑它。

答案 2 :(得分:9)

启发式地,你最好根据字符串大小将它们分开。

伪代码:

returnvalue = false
if len(a) == len(b)
   if len(a) < threshold
      returnvalue = (sorted(a) == sorted(b))
   else
       returnvalue = naminsmethod(a, b)
return returnvalue

如果性能至关重要,字符串大小可能很大或很小,那么这就是我要做的事情。

根据输入大小或类型分割这样的东西是很常见的。算法有不同的优点或缺点,使用另一个更好的方法是愚蠢的......在这种情况下,Namin的方法是O(n),但是具有比O(n log n)排序方法更大的常数因子。

答案 3 :(得分:7)

我认为第一种是“明显”的方式。它更短,更清晰,并且在许多情况下可能更快,因为Python的内置排序是高度优化的。

答案 4 :(得分:5)

你的第二个例子实际上不起作用:

all(a.count(char) == b.count(char) for char in a)

仅在b不包含不在a中的额外字符时才有效。如果字符串中的字符重复,它也会重复工作。

如果您想知道两个字符串是否是相同唯一字符的排列,只需执行以下操作:

set(a) == set(b)

纠正你的第二个例子:

all(str1.count(char) == str2.count(char) for char in set(a) | set(b))

set()对象重载按位OR运算符,以便它将计算为两个集合的并集。这将确保您只为每个字符循环遍历两个字符串的所有字符。

也就是说,sorted()方法更简单,更直观,也是我会使用的方法。

答案 5 :(得分:3)

以下是使用两种不同方法对非常小的字符串执行的一些定时执行:
分拣 2.计数(特别是@namin的原始方法)。

a, b, c = 'confused', 'unfocused', 'foncused'

sort_method = lambda x,y: sorted(x) == sorted(y)

def count_method(a, b):
    d = {}
    for x in a:
        d[x] = d.get(x, 0) + 1
    for x in b:
        d[x] = d.get(x, 0) - 1
    for v in d.itervalues():
        if v != 0:
            return False
    return True

超过100,000个循环的2种方法的平均运行时间为:

不匹配(字符串a和b)

$ python -m timeit -s 'import temp' 'temp.sort_method(temp.a, temp.b)'
100000 loops, best of 3: 9.72 usec per loop
$ python -m timeit -s 'import temp' 'temp.count_method(temp.a, temp.b)'
10000 loops, best of 3: 28.1 usec per loop

匹配(字符串a和c)

$ python -m timeit -s 'import temp' 'temp.sort_method(temp.a, temp.c)'
100000 loops, best of 3: 9.47 usec per loop
$ python -m timeit -s 'import temp' 'temp.count_method(temp.a, temp.c)'
100000 loops, best of 3: 24.6 usec per loop

请记住,使用的字符串非常小。方法的时间复杂度不同,因此您会看到非常大的字符串的不同结果。根据您的数据选择,您甚至可以使用两者的组合。

答案 6 :(得分:2)

我在Java中用我所拥有的书中的所有单词进行了非常彻底的比较。计数方法以各种方式击败排序方法。结果:

Testing against 9227 words.

Permutation testing by sorting ... done.        18.582 s
Permutation testing by counting ... done.       14.949 s

如果有人想要算法和测试数据集,请注释掉。

答案 7 :(得分:2)

首先,为了解决这些问题,例如无论字符串1和字符串2是否完全相同,您可以轻松地使用“if”,因为它是O(1)。 其次,重要的是要考虑它们是否只是数值还是字符串中的单词。如果后一个为真(单词和数值同时在字符串中),则第一个解决方案将不起作用。您可以使用“ord()”函数来增强它,使其成为ASCII数值。但是,最后,你正在使用sort;因此,在最坏的情况下,您的时间复杂度将为O(NlogN)。这个时间复杂性也不错。但是,你可以做得更好。你可以把它做成O(N)。 我的“建议”是使用Array(列表)并同时设置。请注意,在Array中查找值需要迭代,因此它的时间复杂度为O(N),但是在集合中搜索值(我猜它是用Python中的HashTable实现的,我不确定)具有O(1)时间复杂度:

def Permutation2(Str1, Str2):

ArrStr1 = list(Str1) #convert Str1 to array
SetStr2 = set(Str2) #convert Str2 to set

ArrExtra = []

if len(Str1) != len(Str2): #check their length
    return False

elif Str1 == Str2: #check their values
    return True

for x in xrange(len(ArrStr1)):
    ArrExtra.append(ArrStr1[x])

for x in xrange(len(ArrExtra)): #of course len(ArrExtra) == len(ArrStr1) ==len(ArrStr2)
    if ArrExtra[x] in SetStr2: #checking in set is O(1)
        continue
    else:
        return False

return True

答案 8 :(得分:2)

很抱歉我的代码不在Python中,我从未使用它,但我确信这可以很容易地转换为python。我相信这比已发布的所有其他示例更快。它也是O(n),但是尽快停止:

public boolean isPermutation(String a, String b) {
    if (a.length() != b.length()) {
        return false;
    }

    int[] charCount = new int[256];
    for (int i = 0; i < a.length(); ++i) {
        ++charCount[a.charAt(i)];
    }

    for (int i = 0; i < b.length(); ++i) {
        if (--charCount[b.charAt(i)] < 0) {
            return false;
        }
    }
    return true;
}

首先,我不使用字典,而是使用大小为256的数组来表示所有字符。访问索引应该快得多。然后当第二个字符串被迭代时,当计数值低于0时,我立即返回false。当第二个循环结束时,你可以确定字符串是一个排列,因为字符串长度相等而且没有更频繁地使用字符在b中与a相比。

答案 9 :(得分:1)

这是python中的martinus代码。它仅适用于ascii字符串:

def is_permutation(a, b):
    if len(a) != len(b):
        return False

    char_count = [0] * 256
    for c in a:
        char_count[ord(c)] += 1

    for c in b:
        char_count[ord(c)] -= 1
        if char_count[ord(c)] < 0:
            return False

    return True

答案 10 :(得分:1)

在Python 3.1 / 2.7中,您只需使用collections.Counter(a) == collections.Counter(b)

但是sorted(a) == sorted(b)仍然是最明显的恕我直言。你在谈论排列 - 改变顺序 - 所以排序是消除这种差异的明显操作。

答案 11 :(得分:1)

选择第一个 - 它更直接,更容易理解。如果你实际上处理的是非常大的字符串并且性能是一个真正的问题,那就不要使用Python,使用像C这样的东西。

就Python的禅而言,只有一种明显的做事方式是指小而简单的事情。显然,对于任何足够复杂的任务,总会有数以万计的小变化。

答案 12 :(得分:0)

此版本比目前为止提供的任何示例都要快,但对于短字符串,它比sorted(x) == sorted(y)慢20%。这取决于用例,但通常使用不同版本的短字符串和长字符串(如@ patros的答案),20%的性能提升不足以证明代码的复杂性。

它不使用len因此它接受任何迭代,因此它甚至适用于不适合内存的数据,例如,给定两个带有许多重复行的大文本文件,它会回答文件是否具有相同的行(行可以按任何顺序排列。)

def isanagram(iterable1, iterable2):
    d = {}
    get = d.get
    for c in iterable1:
        d[c] = get(c, 0) + 1
    try:
        for c in iterable2:
            d[c] -= 1
        return not any(d.itervalues())
    except KeyError:
        return False

目前还不清楚为什么这个版本比defaultdict(@ namin)快一个大iterable1(在25MB词库中测试)。

如果我们将循环中的get替换为try: ... except KeyError,则对短字符串执行速度会慢2倍,即重复次数较少时。

答案 13 :(得分:0)

这是我一周前写的PHP函数,它检查两个单词是否是字谜。如何比较建议的其他方法(如果在python中实现相同)?评论

public function is_anagram($word1, $word2) {
    $letters1 = str_split($word1);
    $letters2 = str_split($word2);
    if (count($letters1) == count($letters2)) {
        foreach ($letters1 as $letter) {
            $index = array_search($letter, $letters2);
            if ($index !== false) {
                unset($letters2[$index]);
            }
            else { return false; }
        }
        return true;
    }
    return false;        
}

这是PHP版本的Python的文字翻译(由JFS提供):

def is_anagram(word1, word2):
    letters2 = list(word2)
    if len(word1) == len(word2):
       for letter in word1:
           try:
               del letters2[letters2.index(letter)]
           except ValueError:
               return False               
       return True
    return False

评论:

    1. The algorithm is O(N**2). Compare it to @namin's version (it is O(N)).
    2. The multiple returns in the function look horrible.

答案 14 :(得分:0)

这源于@patros' answer

from collections import Counter

def is_anagram(a, b, threshold=1000000):
    """Returns true if one sequence is a permutation of the other.

    Ignores whitespace and character case.
    Compares sorted sequences if the length is below the threshold,
    otherwise compares dictionaries that contain the frequency of the
    elements.
    """
    a, b = a.strip().lower(), b.strip().lower()
    length_a, length_b = len(a), len(b)
    if length_a != length_b:
        return False
    if length_a < threshold:
        return sorted(a) == sorted(b)
    return Counter(a) == Counter(b)  # Or use @namin's method if you don't want to create two dictionaries and don't mind the extra typing.

答案 15 :(得分:0)

在Swift(或其他语言实现)中,您可以查看编码值(在本例中为Unicode)并查看它们是否匹配。

类似的东西:

let string1EncodedValues = "Hello".unicodeScalars.map() {
//each encoded value
$0
//Now add the values
}.reduce(0){ total, value in
    total + value.value
}

let string2EncodedValues = "oellH".unicodeScalars.map() {
    $0
    }.reduce(0) { total, value in
    total + value.value
}

let equalStrings = string1EncodedValues == string2EncodedValues ? true : false

您需要根据需要处理空格和案例。

答案 16 :(得分:0)

def matchPermutation(s1, s2):
  a = []
  b = []

  if len(s1) != len(s2):
    print 'length should be the same'
  return

  for i in range(len(s1)):
    a.append(s1[i])

  for i in range(len(s2)):
    b.append(s2[i])

  if set(a) == set(b):
    print 'Permutation of each other'
  else:
    print 'Not a permutation of each other'
  return

#matchPermutaion('rav', 'var') #returns True
matchPermutaion('rav', 'abc') #returns False

答案 17 :(得分:0)

这是Python中使用哈希与字典的O(n)解决方案。请注意,我不使用默认字典,因为如果我们在检查第二个字母后确定两个字符串不是排列,代码就可以这样停止。

def if_two_words_are_permutations(s1, s2):
    if len(s1) != len(s2):
        return False
    dic = {}
for ch in s1:
    if ch in dic.keys():
        dic[ch] += 1
    else:
        dic[ch] = 1
for ch in s2:
    if not ch in dic.keys():
        return False
    elif dic[ch] == 0:
        return False
    else:
        dic[ch] -= 1
return True

答案 18 :(得分:0)

检查Python中的两个字符串是否相互排列

# First method
def permutation(s1,s2):
 if len(s1) != len(s2):return False;
 return ' '.join(sorted(s1)) == ' '.join(sorted(s2))


# second method
def permutation1(s1,s2):
 if len(s1) != len(s2):return False;
 array = [0]*128;
 for c in s1:
 array[ord(c)] +=1
 for c in s2:
   array[ord(c)] -=1
   if (array[ord(c)]) < 0:
     return False
 return True

答案 19 :(得分:0)

这样的事情怎么样?非常简单易读。这是针对字符串的,因为根据OP。

鉴于sorted()的复杂性为O(n log n)。

def checkPermutation(a,b):
    # input: strings a and b
    # return: boolean true if a is Permutation of b

    if len(a) != len(b):
        return False
    else:
        s_a = ''.join(sorted(a))
        s_b = ''.join(sorted(b))
        if s_a == s_b:
            return True
        else:
            return False

# test inputs
a = 'sRF7w0qbGp4fdgEyNlscUFyouETaPHAiQ2WIxzohiafEGJLw03N8ALvqMw6reLN1kHRjDeDausQBEuIWkIBfqUtsaZcPGoqAIkLlugTxjxLhkRvq5d6i55l4oBH1QoaMXHIZC5nA0K5KPBD9uIwa789sP0ZKV4X6'
b = 'Vq3EeiLGfsAOH2PW6skMN8mEmUAtUKRDIY1kow9t1vIEhe81318wSMICGwf7Rv2qrLrpbeh8bh4hlRLZXDSMyZJYWfejLND4u9EhnNI51DXcQKrceKl9arWqOl7sWIw3EBkeu7Fw4TmyfYwPqCf6oUR0UIdsAVNwbyyiajtQHKh2EKLM1KlY6NdvQTTA7JKn6bLInhFvwZ4yKKbzkgGhF3Oogtnmzl29fW6Q2p0GPuFoueZ74aqlveGTYc0zcXUJkMzltzohoRdMUKP4r5XhbsGBED8ReDbL3ouPhsFchERvvNuaIWLUCY4gl8OW06SMuvceZrCg7EkSFxxprYurHz7VQ2muxzQHj7RG2k3khxbz2ZAhWIlBBtPtg4oXIQ7cbcwgmBXaTXSBgBe3Y8ywYBjinjEjRJjVAiZkWoPrt8JtZv249XiN0MTVYj0ZW6zmcvjZtRn32U3KLMOdjLnRFUP2I3HJtp99uVlM9ghIpae0EfC0v2g78LkZE1YAKsuqCiiy7DVOhyAZUbOrRwXOEDHxUyXwCmo1zfVkPVhwysx8HhH7Iy0yHAMr0Tb97BqcpmmyBsrSgsV1aT3sjY0ctDLibrxbRXBAOexncqB4BBKWJoWkQwUZkFfPXemZkWYmE72w5CFlI6kuwBQp27dCDZ39kRG7Txs1MbsUnRnNHBy1hSOZvTQRYZPX0VmU8SVGUqzwm1ECBHZakQK4RUquk3txKCqbDfbrNmnsEcjFaiMFWkY3Esg6p3Mm41KWysTpzN6287iXjgGSBw6CBv0hH635WiZ0u47IpUD5mY9rkraDDl5sDgd3f586EWJdKAaou3jR7eYU7YuJT3RQVRI0MuS0ec0xYID3WTUI0ckImz2ck7lrtfnkewzRMZSE2ANBkEmg2XAmwrCv0gy4ExW5DayGRXoqUv06ZLGCcBEiaF0fRMlinhElZTVrGPqqhT03WSq4P97JbXA90zUxiHCnsPjuRTthYl7ZaiVZwNt3RtYT4Ff1VQ5KXRwRzdzkRMsubBX7YEhhtl0ZGVlYiP4N4t00Jr7fB4687eabUqK6jcUVpXEpTvKDbj0JLcLYsneM9fsievUz193f6aMQ5o5fm4Ilx3TUZiX4AUsoyd8CD2SK3NkiLuR255BDIA0Zbgnj2XLyQPiJ1T4fjStpjxKOTzsQsZxpThY9Fvjvoxcs3HAiXjLtZ0TSOX6n4ZLjV3TdJMc4PonwqIb3lAndlTMnuzEPof2dXnpexoVm5c37XQ7fBkoMBJ4ydnW25XKYJbkrueRDSwtJGHjY37dob4jPg0axM5uWbqGocXQ4DyiVm5GhvuYX32RQaOtXXXw8cWK6JcSUnlP1gGLMNZEGeDXOuGWiy4AJ7SH93ZQ4iPgoxdfCuW0qbsLKT2HopcY9dtBIRzr91wnES9lDL49tpuW77LSt5dGA0YLSeWAaZt9bDrduE0gDZQ2yX4SDvAOn4PMcbFRfTqzdZXONmO7ruBHHb1tVFlBFNc4xkoetDO2s7mpiVG6YR4EYMFIG1hBPh7Evhttb34AQzqImSQm1gyL3O7n3p98Kqb9qqIPbN1kuhtW5mIbIioWW2n7MHY7E5mt0'

print(checkPermutation(a, b)) #optional

答案 20 :(得分:0)

def permute(str1,str2):
    if sorted(str1) == sorted(str2):
        return True
    else:
        return False

str1="hello"
str2='olehl'
a=permute(str1,str2)
print(a

答案 21 :(得分:0)

from collections import defaultdict
def permutation(s1,s2):
    h = defaultdict(int)
    for ch in s1:
        h[ch]+=1
    for ch in s2:
        h[ch]-=1
    for key in h.keys():
        if h[key]!=0 or len(s1)!= len(s2):
            return False
        return True
print(permutation("tictac","tactic"))