【问题标题】:Longest common prefix using buffer?使用缓冲区的最长公共前缀?
【发布时间】:2011-12-25 19:20:29
【问题描述】:

如果我有一个输入字符串和一个数组:

s = "to_be_or_not_to_be" 
pos = [15, 2, 8]

我试图在引用原始s 的数组pos 的连续元素之间找到最长的公共前缀。我正在尝试获得以下输出:

longest = [3,1]

我得到这个的方法是通过计算以下对的最长公共前缀:

  • s[15:]_bes[2:] 这是 _be_or_not_to_be 给 3 (_be)
  • s[2:]_be_or_not_to_bes[8:] 这是 _not_to_be 给 1 (_)

但是,如果s 很大,我不想在执行s[x:] 之类的操作时创建多个副本。经过数小时的搜索,我找到了函数buffer,它只保留了输入字符串的一个副本,但我不确定在这种情况下使用它的最有效方法是什么。关于如何实现这一目标的任何建议?

【问题讨论】:

    标签: python string text buffer


    【解决方案1】:

    我认为您对副本的担忧是没有根据的。见下文:

    >>> s = "how long is a piece of string...?"
    >>> t = s[12:]
    >>> print t
    a piece of string...?
    >>> id(t[0])
    23295440
    >>> id(s[12])
    23295440
    >>> id(t[2:20]) == id(s[14:32])
    True
    

    除非您复制切片并留下对副本的引用,否则我认为这不会导致任何问题。


    编辑: 字符串实习的技术细节和我自己不太清楚的东西。但我确信字符串切片不是总是副本:

    >>> x = 'google.com'
    >>> y = x[:]
    >>> x is y
    True
    

    我想我想给出的答案是让 python 自己管理它的内存,首先,如果需要,您可以稍后查看内存缓冲区和视图。如果这已经是您遇到的真正问题,请使用实际问题的详细信息更新您的问题。

    【讨论】:

    • 嗯...对不起,我出来无知。这篇文章告诉我一个不同的故事:stackoverflow.com/questions/3422685/… 请查看已接受答案的 cmets 部分。我错过了什么吗?
    • 猜猜我没有读你的最后一行。 +1 感谢您的澄清。
    • @Legend 只保留短字符串,所以如果你的字符串真的很长,切片实际上会创建副本。
    • @agf: Of.. 这就是我想要的。谢谢!在那种情况下,我猜buffer 实际上是在没有创建副本的情况下引用了原始字符串?在使用缓冲区后,我观察到我在解决方案中放入的代码大大加快了速度。
    • 这是 Guido 在 Python-Dev 上的一篇相关文章,该线程的其余部分是一个有趣的阅读。 mail.python.org/pipermail/python-dev/2008-May/079700.html
    【解决方案2】:

    使用buffer 的一种方法如下。但是,可能有更快的方法。

    s = "to_be_or_not_to_be" 
    pos = [15, 2, 8]
    
    lcp = []
    length = len(pos) - 1
    
    for index in range(0, length):
        pre = buffer(s, pos[index])
        cur = buffer(s, pos[index+1], pos[index+1]+len(pre))
    
        count = 0
    
        shorter, longer = min(pre, cur), max(pre, cur)
    
        for i, c in enumerate(shorter):
            if c != longer[i]:
                break
            else:
                count += 1
    
        lcp.append(count)
        print 
    
    print lcp
    

    【讨论】:

    • 如果你坚持使用buffer,你可以使用os.path.commonprefix([buffer(s, i) for i in pos])
    【解决方案3】:
    >>> import os
    >>> os.path.commonprefix([s[i:] for i in pos])
    '_'
    

    让 Python 为你管理内存。不要过早优化。

    要获得您可以做的确切输出(如@agf suggested):

    print [len(commonprefix([buffer(s, i) for i in adj_indexes]))
           for adj_indexes in zip(pos, pos[1:])]
    # -> [3, 1]
    

    【讨论】:

      【解决方案4】:

      这是一种没有buffer 的方法,它不会复制,因为它一次只查看一个字符:

      from itertools import islice, izip
      
      s = "to_be_or_not_to_be"
      pos = [15, 2, 8]
      
      
      length = len(s)    
      
      for start1, start2 in izip(pos, islice(pos, 1, None)):
          pref = 0
          for pos1, pos2 in izip(xrange(start1, length), xrange(start2, length)):
              if s[pos1] == s[pos2]:
                  pref += 1
              else:
                  break
          print pref
      # prints 3 1
      

      我使用 isliceizipxrange 以防您谈论可能非常长的字符串。

      我也无法抗拒这种甚至不需要任何索引的“One Liner”:

      [next((i for i, (a, b) in 
          enumerate(izip(islice(s, start1, None), islice(s, start2, None))) 
              if a != b), 
          length - max((start1, start2))) 
       for start1, start2 in izip(pos, islice(pos, 1, None))]
      

      最后一种方法,使用os.path.commonprefix

      [len(commonprefix((buffer(s, n), buffer(s, m)))) for n, m in zip(pos, pos[1:])]
      

      【讨论】:

      • +1 谢谢。让我检查一下这个 sn-p 的性能并尽快回来。它绝对看起来很酷! :)
      • 你的commonprefix()解决方案太复杂,见my comment
      • @J.F.Sebastian 我看到了你的评论;这是不正确的。他想要的输出是[3, 1],而不是_。他希望只考虑前两个位置,然后只考虑后两个,您的版本同时考虑所有三个
      猜你喜欢
      • 2021-09-11
      • 1970-01-01
      • 2022-11-22
      • 2020-07-05
      • 2018-09-30
      • 2013-04-14
      • 2012-02-01
      • 2020-02-10
      • 2021-10-12
      相关资源
      最近更新 更多