【问题标题】:Python Iteration Problem!Python 迭代问题!
【发布时间】:2010-01-31 20:41:58
【问题描述】:

我这里有这段代码,它应该从列表 n1 和 n2 中删除常用字母。但是当我运行这段代码时,它只运行一次,因为它只从 n1 和 n2 中删除 'a' 而不会删除 'k'。

只是为了澄清这段代码应该始终只适用于 2 个单词。

name1 = "abdjek"
name2 = "doarhsnk"

n1l = list(name1)
n2l = list(name2)

for i in range(len(n1l)):
   for j in range(len(n2l)):
         if n1l[i] == n2l[j]:
               n1l.pop(i)
               n2l.pop(j)
               n1l.append('0')
               n2l.append('1')

好的,等等,它似乎适用于上述 2 个名称,但是当我有 name1 = "naveen" 和 name2 = "darshana" 时它不起作用!

【问题讨论】:

  • 当我运行它时,它会产生 n1l = ['b', 'j', 'e', '0', '0', '0'] 和 n2l = [ 'o', 'r', 'h', 's', 'n', '1', '1', '1'].
  • 我刚刚将它粘贴到我的 python 解释器中,它似乎做了你想做的事情(删除重复的字母并添加 0 或 1 以保持列表的长度相同)。不过,这不是我会使用的算法。
  • 使用集合运算符可以更有效地完成此操作。
  • 还有更多 Pythonic 方法可以做到这一点。我要更改的最明显的事情是: 1. 字符串实际上是字符序列,因此您不需要显式创建列表。 2. for ch in aString: 然后使用ch 比for i in range(len(aString)): 然后使用aString[i] 更可取。
  • 如果 name1 中有 3 个字母出现,而 name 2 中只有 1 个呢?它们都被删除了吗?

标签: python iteration


【解决方案1】:

我建议一个更简单的方法:

def removecommon(name1, name2):
  common = set(name1).intersection(name2)
  res1 = ''.join(n for n in name1 if n not in common)
  res2 = ''.join(n for n in name2 if n not in common)
  return res1, res2

n1, n2 = removecommon('naveen', 'darshana')
print n1, n2

根据需要发出vee drsh。

编辑:正如 OP 现在指定的(在评论中 -- 请记住也编辑您的 question,哦,OP!)他实际上只想删除第一次出现在每个普通字母的每个单词中,所需要的算法当然是完全不同的。一个简单的方法(如果单词的长度不太长也可以):

def removefirstcommon(name1, name2):
  common = set(name1).intersection(name2)
  n1 = list(name1)
  for c in common: n1.remove(c)
  n2 = list(name2)
  for c in common: n2.remove(c)
  return ''.join(n1), ''.join(n2)

更精细的方法(虽然对于正常长度的单词来说较慢)对于极长的单词会更快(因为以下是 O(N) 而前者是 O(N squared)):

def removefirstcommonlongwords(name1, name2):
  common = set(name1).intersection(name2)
  def mustrem(c, copycom):
    res = c not in copycom
    copycom.discard(c)
    return res
  cop = set(common)
  n1 = [c for c in name1 if mustrem(c, cop)]
  n2 = [c for c in name2 if mustrem(c, common)]
  return ''.join(n1), ''.join(n2)

【讨论】:

  • @Alex Martelli 感谢这种新方法,但我还有 1 个问题,我只需要删除匹配的第一个实例,我必须输出 'veen' 和 'darsha'。我不想删除 'n' 和 'a' 的所有实例
  • @Prabhu,如何通过从“darshana”中删除第一次出现的 a 和 n 来获得“darsha”? 'drshaa' 似乎遵循“删除第一次出现”规则。请澄清一下?
【解决方案2】:

更pythonic 的方法是使用sets 和列表推导。

name1 = "naveen"; name2 = "darshana"

name1_set=set(name1)
name2_set=set(name2)

clean1=[x for x in  name1 if x not in name2_set]
clean2=[x for x in name2 if x not in name1_set]

clean1.extend(['0']*(len(name1)-len(clean1)))
clean2.extend(['1']*(len(name2)-len(clean2)))

print clean1,clean2

set 为我们提供了 O(1) 次查找,因此通过使其 O(N) 而不是 O(N^2) 来加快整个过程。

编辑:鉴于您后来评论出现次数很重要,这是考虑到这一点的更新版本。

name1 = "naveen"; name2 = "darshana"

count1={}
count2={}


for x in name1:
    count1[x]=count1.get(x,0)+1

for x in name2:
    count2[x]=count2.get(x,0)+1

def remove_dups(name,count,null):
    clean=[]
    for x in name:
        if count.get(x,0):
            count[x]-=1
        else:
            clean.append(x)
    clean.extend([null]*(len(name)-len(clean)))
    return clean

clean1=remove_dups(name1,count2,'0')
clean2=remove_dups(name2,count1,'1')

print clean1,clean2

它使用dicts 来记录出现次数。每当删除一个字符时,另一个名称的相应计数就会减少。复杂度仍然是 O(N)。

它打印['v', 'e', 'e', 'n', '0', '0'] 和['d', 'r', 's', 'h', 'a', 'a', '1', '1']。这是你想要的吗?

【讨论】:

  • set 采用可迭代对象,因此 set('foo') 有效,无需转换为中间列表。
  • @Mike:对。忘记了字符串已经是可迭代的。答案已更新,谢谢。
【解决方案3】:

它在这里为我工作。也就是说,如果我这样添加打印语句:

name1 = "abdjek"
name2 = "doarhsnk"

n1l = list(name1)
n2l = list(name2)

print "Lists before loop:"
print n1l
print n2l

for i in range(len(n1l)):
    for j in range(len(n2l)):
        if n1l[i] == n2l[j]:
           n1l.pop(i)
           n2l.pop(j)
           n1l.append('0')
           n2l.append('1')

print "Lists after loop:"
print n1l
print n2l

字符“a”、“d”和“k”都被删除:

> python test.py 
Lists before loop:
['a', 'b', 'd', 'j', 'e', 'k']
['d', 'o', 'a', 'r', 'h', 's', 'n', 'k']
Lists after loop:
['b', 'j', 'e', '0', '0', '0']
['o', 'r', 'h', 's', 'n', '1', '1', '1']

【讨论】:

  • 好的,我认为它可以工作......但是当我有 name1 = "naveen" name2 = "darshana" 它不起作用!
【解决方案4】:

您的代码可能无法按预期工作,因为它正在删除成对的字母。例如,您看到一个 a,然后从您的单词中删除两个 a...

【讨论】:

    【解决方案5】:

    您的代码很可能会失败,因为您从列表中的任何位置弹出常用字母,但将替换项(“0”和“1”)附加到列表的end。它们应该分别位于 i 和 j 位置。

    所以循环应该是这样的:

    for i in range(len(n1l)):
       for j in range(len(n2l)):
             if n1l[i] == n2l[j] and n1l[i] not in ("0", "1"):
                   print "common letter ", n1l[i]
                   # Replace i-th and j-th element
                   n1l[i] = "0"
                   n2l[j] = "1"
    

    无论如何,还有更多“pythonic”方式,其他答案中已经显示。

    编辑:经过测试并与name1 = "naveen" / name2 = "darshana" 一起使用。

    【讨论】:

      【解决方案6】:

      这里有一些运行时间为 O(n) 的相当(恕我直言,相当优雅)的代码。如果单词 1 有 N 次出现字母 x,它会从单词 2 中删除前 N 个 x(反之亦然)——我认为这就是你想要的,但我可能错了。

      from collections import defaultdict
      
      def build(s, chars_s, chars_t):
          """Return characters of s, with duplicate characters from t removed."""
          for i, char in enumerate(s):
              indexes_s, indexes_t = chars_s[char], chars_t[char]
              if len(indexes_s) > len(indexes_t) and i >= indexes_s[len(indexes_t)]:
                  yield char
      
      def rm_dup(a, b):
          """Pairwise remove duplicate letters in a and b."""
          chars_a, chars_b = defaultdict(list), defaultdict(list)
          for i, char in enumerate(a): chars_a[char].append(i)
          for i, char in enumerate(b): chars_b[char].append(i)
          return (''.join(build(a, chars_a, chars_b)),
                  ''.join(build(b, chars_b, chars_a)))
      
      print rm_dup('naveen', 'darshana')
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-15
        • 2021-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-14
        • 2016-10-29
        • 2014-09-23
        相关资源
        最近更新 更多