6

所以我有一个有趣的问题。

我正在尝试写一些乱七八糟的单词,我需要知道我用过哪些字母,哪些没用过。到目前为止,我有以下代码:

def remove_common(x,y):
   sort = sort = lambda x: "".join(c for c in sorted(x.lower()) if c.isalpha())
   x,y  = sort(x), sort(y)
   //some code that removes y from x
   return leftovers

print remove_common("Lets chat about code","that cool cat")
print remove_common("A silly word","a lil sword")
print remove_common("The Thing","Height")

>>> "bdeesu"
>>> "iy"
>>> "tn"

我正在寻找一种简单的方法来删除两者中的字母,但在必要时保留重复项。

  1. 字符串转为小写,非字母被删除
  2. 重复很重要,所以如果x = "aa"y = "a",那么结果是"a",不是""。我认为这排除了使用集合。
  3. 如果一个字母在 y 中而不在 x 中,它应该大声说出来。
  4. 速度并不重要,但代码的优雅才是重要的。所以代码越好阅读越好——我知道这是一种主观的感觉。
  5. 输出顺序并不重要,因为我可以将其转换为字符串sorted()

我查看了其他答案,但这些主要与仅给出未出现在一个中的字母并删除重复项有关。

4

3 回答 3

7

您可以使用collections.Counter可以相互减去的对象:

import collections

def remove_common(x,y):
    count = lambda x: collections.Counter(c for c in x.lower() if c.isalpha())
    cx, cy = count(x), count(y)
    diff  = cx - cy
    rev_diff = cy - cx
    assert len(rev_diff) == 0, "%s in y and not x" % "".join(rev_diff.elements())

    return "".join(sorted(diff.elements()))

作为正在发生的事情的演示:

>>> c1 = collections.Counter("hello world")
>>> c2 = collections.Counter("hey worlds")
>>> c1 - c2
Counter({'l': 2, 'o': 1})
>> (c1 - c2).elements()
['l', 'l', 'o']
于 2013-06-29T02:13:53.697 回答
1

collections.Counter如果您使用运算符,则不会让任何计数低于零-。但是,如果您使用 c.subtract(d),那么它将允许您这样做。此外,当使用 c.elements() 时,会忽略计数为负的值。

这是一个基于 collections.Counter 的实现:

import collections                                                              

def remove_common(x, y):                                                        
    sort = lambda x: "".join(c for c in sorted(x.lower()) if c.isalpha())       
    x, y = list(sort(x)), list(sort(y))                                         
    cx = collections.Counter(x)                                                 
    cy = collections.Counter(y)
    cx.subtract(cy)

    result = ""
    for letter, count in cx.iteritems():
        for i in range(abs(count)):
            result += letter

    return result      

我在以下测试集上运行它:

print remove_common("Lets chat about code","that cave")                     
print remove_common("basdf aa", "a basd")                                   
print remove_common("asdfq", "asdf")                                        
print remove_common("asdf", "asdfq")                                        

print remove_common("aa bb s", "a bbb")

结果:

cbedloosutv
af
q
q
asb

要检测在 y 中但不在 x 中的字母,您应该将 的结果与cy.subtract(cx)的值进行比较cy。例如:

cz = collections.Counter(cy) # because c.subtract(..) modifies c
cz.subtract(cx)
for letter, count in cz.iteritems():
    if count == cy[letter]: # in the case that there were none of letter in x
        assert False

如果 y 中存在一个字母但重复次数多于 x 中的次数,我见过的其他解决方案也会失败(例如:'hi there' 和 'hii' 会在 Josh Smeaton 的解决方案中产生 AssertionError 但不是这个一)。您的要求在这方面 IMO 有点模棱两可。不过,stackoverflow 的美妙之处在于有足够的答案来挑选你的毒药。

希望这可以帮助。

于 2013-06-29T02:48:29.440 回答
1

基于大卫罗宾逊的回答:

import collections.Counter as C
def remove_common(x,y):
    s1,s2=filter(str.isalpha, x.lower()),filter(str.isalpha, y.lower())
    c1,c2 = C(s1),C(s2)
    if any(c2-c1):
        assert False
    return list((c1-c2).elements())

>>> remove_common('hi there','hi')
['h', 'r', 'e', 'e', 't']
>>> x,y='Lets chat about code','that cool cat'
>>> remove_common(x,y)
['u', 's', 'b', 'e', 'e', 'd']
>>> remove_common('hi','ho')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in remove_common
AssertionError
于 2013-06-29T02:49:29.787 回答