【问题标题】:Count consecutive characters计算连续字符
【发布时间】:2015-12-23 21:24:21
【问题描述】:

如何计算 Python 中的连续字符以查看每个唯一数字在下一个唯一数字之前重复的次数?

起初,我以为我可以这样做:

word = '1000'

counter=0
print range(len(word))


for i in range(len(word)-1):
    while word[i]==word[i+1]:
        counter +=1
        print counter*"0"
    else:
        counter=1
        print counter*"1"

这样我就可以看到每个唯一数字重复的次数。但是,当i 达到最后一个值时,这当然会超出范围。

在上面的示例中,我希望 Python 告诉我 1 重复 1,而 0 重复 3 次。但是,由于我的 while 语句,上面的代码失败了。

我知道您可以只使用内置函数来做到这一点,并且更喜欢这种方式的解决方案。

【问题讨论】:

  • 使用len(word) - 1有什么问题?还会认为您需要将 counter 初始化为 1
  • 好吧,这确实有很大帮助......我会继续努力,看看我是否能想出一个解决方案!
  • 为什么不添加另一个 if 子句来检查 i 是否大于 len(word)
  • 如果你的字符串是'100011',你希望输出是什么?我的回答假设[("1", 1), ("0", 3), ("1", 2)],但也许你想要比这更细微的东西?

标签: python string count


【解决方案1】:

连续计数:

哦,还没有人发布itertools.groupby

s = "111000222334455555"

from itertools import groupby

groups = groupby(s)
result = [(label, sum(1 for _ in group)) for label, group in groups]

之后,result 看起来像:

[("1": 3), ("0", 3), ("2", 3), ("3", 2), ("4", 2), ("5", 5)]

您可以使用以下格式进行格式化:

", ".join("{}x{}".format(label, count) for label, count in result)
# "1x3, 0x3, 2x3, 3x2, 4x2, 5x5"

总数:

cmets 中的某个人担心您想要 total 个数字,所以"11100111" -> {"1":6, "0":2}。在这种情况下,您想使用collections.Counter

from collections import Counter

s = "11100111"
result = Counter(s)
# {"1":6, "0":2}

你的方法:

正如许多人指出的那样,您的方法失败了,因为您正在循环通过 range(len(s)) 但寻址 s[i+1]。当i 指向s 的最后一个索引时,这会导致一个错误,因此i+1 会引发IndexError。解决此问题的一种方法是循环遍历 range(len(s)-1),但生成一些要迭代的内容更加 Pythonic。

对于不是绝对巨大的字符串,zip(s, s[1:]) 不是性能问题,因此您可以这样做:

counts = []
count = 1
for a, b in zip(s, s[1:]):
    if a==b:
        count += 1
    else:
        counts.append((a, count))
        count = 1

唯一的问题是,如果最后一个字符是唯一的,您必须对其进行特殊处理。这可以通过itertools.zip_longest修复

import itertools

counts = []
count = 1
for a, b in itertools.zip_longest(s, s[1:], fillvalue=None):
    if a==b:
        count += 1
    else:
        counts.append((a, count))
        count = 1

如果您确实有一个真正的 巨大 字符串并且无法忍受一次将其中两个保存在内存中,您可以使用itertools recipe pairwise

def pairwise(iterable):
    """iterates pairwise without holding an extra copy of iterable in memory"""
    a, b = itertools.tee(iterable)
    next(b, None)
    return itertools.zip_longest(a, b, fillvalue=None)

counts = []
count = 1
for a, b in pairwise(s):
    ...

【讨论】:

  • @baldr 感谢list 演员编辑。我忘了它给你一些时髦的观点,而不是没有定义__len__。在这些情况下,我更喜欢对生成器求和,而不是建立一个要扔掉的列表,但是 YMMV
  • 这不只是计数吗?如果你把一些数字分开,比如11122111
  • @ray 然后你得到[("1", 3), ("2", 2), ("1", 3)]。 OP 想要 连续 个字符。
  • @AdamSmith:我给了你我的 +1。我认为您的答案很好,并且使用内置功能也更好。至少我的理解(在 OP 编辑​​之前)OP 对算法感兴趣,而不是内置的方法。
  • 这是一个有点旧的帖子,但我正在尝试这个zip_longest 解决方案,并注意到重置计数count = 1 也是必要的,因为最后一个else 语句(就像在zip) 的情况下,否则结果不正确。
【解决方案2】:

“那种方式”的解决方案,只有基本的陈述:

word="100011010" #word = "1"
count=1
length=""
if len(word)>1:
    for i in range(1,len(word)):
       if word[i-1]==word[i]:
          count+=1
       else :
           length += word[i-1]+" repeats "+str(count)+", "
           count=1
    length += ("and "+word[i]+" repeats "+str(count))
else:
    i=0
    length += ("and "+word[i]+" repeats "+str(count))
print (length)

输出:

'1 repeats 1, 0 repeats 3, 1 repeats 2, 0 repeats 1, 1 repeats 1, and 0 repeats 1'
#'1 repeats 1'

【讨论】:

    【解决方案3】:

    总计(无子分组)

    #!/usr/bin/python3 -B
    
    charseq = 'abbcccdddd'
    distros = { c:1 for c in charseq  }
    
    for c in range(len(charseq)-1):
        if charseq[c] == charseq[c+1]:
            distros[charseq[c]] += 1
    
    print(distros)
    

    我将对有趣的行进行简要说明。

    distros = { c:1 for c in charseq  }
    

    上面的行是一个字典推导,它基本上遍历charseq中的字符,并为字典创建一个键/值对,其中键是字符,值是遇到的次数到目前为止。

    然后是循环:

    for c in range(len(charseq)-1):
    

    我们从0 转到length - 1 以避免超出循环体中的c+1 索引。

    if charseq[c] == charseq[c+1]:
        distros[charseq[c]] += 1
    

    此时,我们知道我们遇到的每个匹配都是连续的,所以我们只需在字符键上加 1。例如,如果我们拍摄一次迭代的快照,代码可能如下所示(出于说明目的,使用直接值而不是变量):

    # replacing vars for their values
    if charseq[1] == charseq[1+1]:
        distros[charseq[1]] += 1
    
    # this is a snapshot of a single comparison here and what happens later
    if 'b' == 'b':
        distros['b'] += 1
    

    您可以在下面看到正确计数的程序输出:

    ➜  /tmp  ./counter.py
    {'b': 2, 'a': 1, 'c': 3, 'd': 4}
    

    【讨论】:

    • 他不需要数数。他要求连续的字符。喜欢:aabbbcdefabca:2, b:3, c:1, d:1, ...
    • for c in zip(sentence, sentence[1:])替换for c in sentence
    • @inspectorG4dget:做了一些不同的事情,因为该更改不适用于以前的代码。
    • @baldr:它产生{'a': 6, 'd': 4, 'f': 2, 'b': 2, 'c': 3}。在我看来没问题。如果它不起作用,您能否更具体地说明您认为问题是什么?
    • @ray,预期输出应该是a:1, b:2, c:3, d:4, a:4, f:2, a:3
    【解决方案4】:

    您只需将len(word) 更改为len(word) - 1。也就是说,您还可以使用 False 的值为 0 和 True 的值为 1 和 sum 的事实:

    sum(word[i] == word[i+1] for i in range(len(word)-1))
    

    这会产生(False, True, True, False) 的总和,其中False 为0,True 为1 - 这就是您所追求的。

    如果你想让它安全,你需要保护空词(索引 -1 访问):

    sum(word[i] == word[i+1] for i in range(max(0, len(word)-1)))
    

    这可以通过zip来改进:

    sum(c1 == c2 for c1, c2 in zip(word[:-1], word[1:]))
    

    【讨论】:

    • 我实际上想做的是这样的:
    【解决方案5】:

    如果我们想统计连续个字符不循环,我们可以使用pandas

    In [1]: import pandas as pd
    
    In [2]: sample = 'abbcccddddaaaaffaaa'
    In [3]: d = pd.Series(list(sample))
    
    In [4]: [(cat[1], grp.shape[0]) for cat, grp in d.groupby([d.ne(d.shift()).cumsum(), d])]
    Out[4]: [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 4), ('f', 2), ('a', 3)]
    

    关键是找到第一个元素与之前的值不同,然后在pandas中进行适当的分组:

    In [5]: sample = 'abba'
    In [6]: d = pd.Series(list(sample))
    
    In [7]: d.ne(d.shift())
    Out[7]:
    0     True
    1     True
    2    False
    3     True
    dtype: bool
    
    In [8]: d.ne(d.shift()).cumsum()
    Out[8]:
    0    1
    1    2
    2    2
    3    3
    dtype: int32
    

    【讨论】:

      【解决方案6】:

      这是我在 python 3 中查找二进制字符串中连续 1 的最大数量的简单代码:

      count= 0
      maxcount = 0
      for i in str(bin(13)):
          if i == '1':
              count +=1
          elif count > maxcount:
              maxcount = count;
              count = 0
          else:
              count = 0
      if count > maxcount: maxcount = count        
      maxcount
      

      【讨论】:

      • 这并不能真正回答问题。 OP想要计算字符串中每个字符的连续字符数。
      【解决方案7】:

      无需计数或分组。只需注意发生变化的索引并减去连续的索引即可。

      w = "111000222334455555"
      iw = [0] + [i+1 for i in range(len(w)-1) if w[i] != w[i+1]] + [len(w)]
      dw = [w[i] for i in range(len(w)-1) if w[i] != w[i+1]] + [w[-1]]
      cw = [ iw[j] - iw[j-1] for j in range(1, len(iw) ) ]
      
      print(dw)  # digits
      ['1', '0', '2', '3', '4']
      print(cw)  # counts
      [3, 3, 3, 2, 2, 5]
      
      w = 'XXYXYYYXYXXzzzzzYYY'
      iw = [0] + [i+1 for i in range(len(w)-1) if w[i] != w[i+1]] + [len(w)]
      dw = [w[i] for i in range(len(w)-1) if w[i] != w[i+1]] + [w[-1]]
      cw = [ iw[j] - iw[j-1] for j in range(1, len(iw) ) ]
      print(dw)  # characters
      print(cw)  # digits
      
      ['X', 'Y', 'X', 'Y', 'X', 'Y', 'X', 'z', 'Y']
      [2, 1, 1, 3, 1, 1, 2, 5, 3]
      

      【讨论】:

        【解决方案8】:

        一个返回连续字符数量且没有导入的单行:

        def f(x):s=x+" ";t=[x[1] for x in zip(s[0:],s[1:],s[2:]) if (x[1]==x[0])or(x[1]==x[2])];return {h: t.count(h) for h in set(t)}
        

        返回列表中任何重复字符在连续字符中出现的次数。

        或者,这完成了同样的事情,尽管要慢得多:

        def A(m):t=[thing for x,thing in enumerate(m) if thing in [(m[x+1] if x+1<len(m) else None),(m[x-1] if x-1>0 else None)]];return {h: t.count(h) for h in set(t)}
        

        在性能方面,我使用了

        site = 'https://web.njit.edu/~cm395/theBeeMovieScript/'
        s = urllib.request.urlopen(site).read(100_000)
        s = str(copy.deepcopy(s))
        print(timeit.timeit('A(s)',globals=locals(),number=100))
        print(timeit.timeit('f(s)',globals=locals(),number=100))
        

        导致:

        12.528256356999918
        5.351301653001428
        

        这种方法肯定可以改进,但不使用任何外部库,这是我能想到的最好的方法。

        【讨论】:

          【解决方案9】:

          在python中

          your_string = "wwwwweaaaawwbbbbn"
          current = ''
          count = 0
          for index, loop in enumerate(your_string):
              current = loop
              count = count + 1
              if index == len(your_string)-1:
                  print(f"{count}{current}", end ='')
                  break
          
              if your_string[index+1] != current:
                  print(f"{count}{current}",end ='')
                  count = 0
                  continue
          

          这将输出

          5w1e4a2w4b1n
          

          【讨论】:

            【解决方案10】:
            #I wrote the code using simple loops and if statement
            s='feeekksssh' #len(s) =11
            count=1  #f:0, e:3, j:2, s:3 h:1
            l=[]
            for i in range(1,len(s)): #range(1,10)
                if s[i-1]==s[i]:
                    count = count+1
                else:
                    l.append(count)
                    count=1
                if i == len(s)-1: #To check the last character sequence we need loop reverse order
                    reverse_count=1
                    for i in range(-1,-(len(s)),-1): #Lopping only for last character
                        if s[i] == s[i-1]:
                            reverse_count = reverse_count+1
                        else:
                            l.append(reverse_count)
                            break
            print(l)
            

            【讨论】:

              【解决方案11】:

              今天我接受了一次采访,被问到同样的问题。我一直在为最初的解决方案苦苦挣扎:

              s = 'abbcccda'
              
              old = ''
              cnt = 0
              res = ''
              for c in s:
                  cnt += 1
                  if old != c:
                      res += f'{old}{cnt}'
                      old = c
                      cnt = 0  # default 0 or 1 neither work
              print(res)
              #  1a1b2c3d1
              

              可悲的是,这个解决方案总是得到意想不到的边缘情况结果(有没有人修复代码?也许我需要发布另一个问题),最后让面试超时。

              面试后我冷静下来,很快就有了我认为的稳定解决方案(虽然我最喜欢groupby)。

              s = 'abbcccda'
              
              olds = []
              for c in s:
                  if olds and c in olds[-1]:
                      olds[-1].append(c)
                  else:
                      olds.append([c])
              print(olds)
              res = ''.join([f'{lst[0]}{len(lst)}' for lst in olds])
              print(res)
              
              #  [['a'], ['b', 'b'], ['c', 'c', 'c'], ['d'], ['a']]
              #  a1b2c3d1a1
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-11-20
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-12-06
                • 2022-01-24
                相关资源
                最近更新 更多