找到字符和数字之间的可能双射

时间:2012-11-21 08:21:42

标签: python list bijection

假设你有一个字符串S和一个列表L中的数字序列,使得len(S)= len(L)。

检查是否可以在字符串的字符与序列中的数字之间找到双射,以使每个字符与一个且仅一个数字匹配时,最简洁的方法是什么。

例如,“aabbcc”应与115522匹配,但不能与123456或111111匹配。

我有一个带有两个dicts和循环的复杂设置,但我想知道是否有一种干净的方法,可能是通过使用Python库中的一些函数。

5 个答案:

答案 0 :(得分:6)

我会使用一套:

In [9]: set("aabbcc")
Out[9]: set(['a', 'c', 'b'])

In [10]: set(zip("aabbcc", [1, 1, 5, 5, 2, 2]))
Out[10]: set([('a', 1), ('c', 2), ('b', 5)])

当且仅当映射是满射时,第二组的长度将等于第一组。 (如果不是,你将有两个字母副本映射到第二组中的相同数字,反之亦然)

以下是实现这个想法的代码

def is_bijection(seq1, seq2):
    distinct1 = set(seq1)
    distinct2 = set(seq2)
    distinctMappings = set(zip(seq1, seq2))
    return len(distinct1) == len(distinctMappings) and len(distinct2) == len(distinctMappings)

如果一个序列比另一个序列短,则返回true,但是已经建立了有效的映射。如果序列长度必须相同,则应添加一个检查。

答案 1 :(得分:0)

import itertools

a = 'aabbcc'
b = 112233

z = sorted(zip(str(a), str(b)))
x = all(
    gx == g0
    for k, g in itertools.groupby(z, key=lambda x: x[0])
    for gx in g for g0 in g
)
print x

或:

import itertools

a = 'aabbcc'
b = 112233

z = zip(str(a), str(b))
x = all(
    (z1[0] == z2[0]) == (z1[1] == z2[1]) for z1 in z for z2 in z
)
print x

答案 2 :(得分:0)

有一种更优雅的方式来实现这一点(使用排序和itertools.groupby),但我很想睡觉 - 现在就把它弄清楚了。但这应该仍然有效:

In [172]: S = "aabbcc"

In [173]: L = [1, 1, 5, 5, 2, 2]

In [174]: mapping = collections.defaultdict(list)

In [175]: reverseMapping = collections.defaultdict(list)

In [176]: for digit, char in zip(L, S):
    mapping[digit].append(char)
    reverseMapping[char].append(digit)
   .....:     

In [177]: all(len(set(v))==1 for v in mapping.values()) and all(len(set(v))==1 for v in reverseMapping.values())
Out[177]: True

In [181]: S = "aabbcc"

In [182]: L = [1, 2, 3, 4, 5, 6]

In [183]: mapping = collections.defaultdict(list)

In [184]: reverseMapping = collections.defaultdict(list)

In [185]: for digit, char in zip(L, S):                                                                         
    mapping[digit].append(char)
    reverseMapping[char].append(digit)
   .....:     

In [186]: all(len(set(v))==1 for v in mapping.values()) and all(len(set(v))==1 for v in reverseMapping.values())
Out[186]: False

希望这有帮助

答案 3 :(得分:0)

尊重订单:

>>> s = "aabbcc"
>>> n = 115522
>>> l1 = dict(zip(s, str(n))).items()
>>> l2 = zip(s, str(n))
>>> l1
[('a', '1'), ('c', '2'), ('b', '5')]
>>> l2
[('a', '1'), ('a', '1'), ('b', '5'), ('b', '5'), ('c', '2'), ('c', '2')]
>>> not bool([i for i in l2 if i not in l1])
True
>>> n = 115225
>>> l1 = dict(zip(s, str(n))).items()
>>> l2 = zip(s, str(n))
>>> not bool([i for i in l2 if i not in l1])
False

答案 4 :(得分:0)

由于您通常只谈论集合之间的双射,我认为,与其他答案不同,数字的顺序不需要与字母的顺序相匹配。如果是这样,那么有一个简短而优雅的解决方案,但它需要collections.Counter类,这是在python 2.7中引入的。对于那些坚持使用旧版本的人来说,有一个backport for 2.5+

from collections import Counter

def bijection_exists_between(a, b):
    return sorted(Counter(a).values()) == sorted(Counter(b).values())

测试:

>>> bijection_exists_between("aabbcc", "123123")
True
>>> bijection_exists_between("aabbcc", "123124")
False

你的例子在边缘情况上相当清淡,因为另一种阅读你的问题的方法允许数字的数量和字母的数量是不相等的(即你从一组独特的字符寻找一个双射的集合。唯一数字,因此例如"aabbcc"会生成"123333"。)。如果这是您的意思,请改用此版本:

def bijection_exists_between(a, b):
    return len(set(a)) == len(set(b))