繁体   English   中英

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

[英]Checking if two strings are permutations of each other in Python

我正在检查两个字符串ab是否是彼此的排列,我想知道在 Python 中执行此操作的理想方法是什么。 从 Python 之禅中,“应该有一种——最好只有一种——明显的方法来做到这一点”,但我认为至少有两种方法:

sorted(a) == sorted(b)

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

但第一个是慢时(例如)的第一个字符a是在无处b ,并且所述第二较慢时它们实际上排列。

有没有更好的方法(无论是在更 Pythonic 的意义上,还是在平均速度更快的意义上)来做到这一点? 或者我应该根据我期望最常见的情况从这两种情况中进行选择吗?

这是 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

“但是当(例如)a 的第一个字符不在 b 中时,第一个会较慢”。

这种退化情况的性能分析不是一个好主意。 这是一个浪费时间的老鼠洞,想出各种晦涩的特殊情况。

只做O型的“整体”分析。

总的来说,排序是O ( n log( n ) )。

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

如果一些晦涩的特殊情况恰好更快——或更慢,那可能很有趣。 但只有当你知道你的晦涩特殊情况的频率时才重要。 在分析排序算法时,重要的是要注意,相当多的排序涉及已经按正确顺序排列的数据(无论是运气还是巧妙的设计),因此对预排序数据的排序性能很重要。

在您晦涩的特殊情况下(“a 的第一个字符在 b 中无处可寻”)这种频率是否足够重要? 如果这只是您想到的特殊情况,请将其搁置一旁。 如果这是关于您的数据的事实,那么请考虑一下。

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

伪代码:

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) 排序方法更大的常数因子。

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

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

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() 方法更简单、更直观,这就是我会使用的方法。

下面是一些对非常小的字符串的定时执行,使用两种不同的方法:
1.排序
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

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

抱歉,我的代码不是用 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 相比。

这是 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

我对 Java 中的所有单词进行了非常彻底的比较。 计数方法在各方面都优于排序方法。 结果:

Testing against 9227 words.

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

如果有人想要算法和测试数据集,请发表评论。

首先,为了解决此类问题,例如字符串 1 和字符串 2 是否完全相同,您可以轻松地使用“if”,因为它是 O(1)。 其次,重要的是要考虑它们是否只是数值或它们也可以是字符串中的单词。 如果后者为真(单词和数值同时在字符串中),则您的第一个解决方案将不起作用。 您可以通过使用“ord()”函数来增强它,使其成为 ASCII 数值。 但是,最终,您使用的是 sort; 因此,在最坏的情况下,您的时间复杂度将是 O(NlogN)。 这个时间复杂度还不错。 但是,你可以做得更好。 你可以把它变成 O(N)。 我的“建议”是使用 Array(list) 并同时设置。 请注意,在 Array 中找到一个值需要迭代,所以它的时间复杂度是 O(N),但是在 set 中搜索一个值(我猜它是用 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

这是从@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.

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

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

使用第一个 - 它更直接,更容易理解。 如果您实际上正在处理非常大的字符串并且性能是一个真正的问题,那么不要使用 Python,使用类似 C 的东西。

就 Python 之禅而言,应该只有一种明显的做事方式是指小而简单的事情。 显然,对于任何足够复杂的任务,在完成它的方法上总会有无数的小变化。

这是 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

在 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

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

这是我大约一周前写的一个 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.

这个版本比目前介绍的任何示例都快,除了比sorted(x) == sorted(y)慢 20% 之外,对于短字符串。 这取决于用例,但通常 20% 的性能增益不足以证明通过对短字符串和长字符串使用不同版本(如@patros 的答案)来证明代码的复杂性是合理的。

它不使用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's) 更快,用于大型iterable1 (在 25MB 同义词库上测试)。

如果我们用try: ... except KeyError代替循环中的get ,那么对于短字符串,即当几乎没有重复项时,它的执行速度会慢 2 倍。

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

在 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

这样的事情怎么样。 非常直接和可读。 这是用于字符串,因为根据 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
def permute(str1,str2):
    if sorted(str1) == sorted(str2):
        return True
    else:
        return False

str1="hello"
str2='olehl'
a=permute(str1,str2)
print(a
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"))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM