【问题标题】:How do I replace text from beginning to end by shortest to longest match如何用最短到最长匹配从头到尾替换文本
【发布时间】:2019-10-30 07:52:30
【问题描述】:

如何用最短到最长匹配从头到尾替换文本

我有数据:

In [1807]: text='110011111000011010110011'                                                                                                                                                                 

还有一个看起来像这样的字典:

s={'11': '++--', '01': '-+-+-', '000': '+-++++++', '001': '--', '100': '--+-', '101': '----++-+--'}

我希望将文本的输出替换为

'++----++--++--+-++++++-+-+-----++-+---+-+---+-++--'

所以第一个11,变成++--,第二个匹配是001,然后是--,第三个匹配是11,然后替换成++--。最短的匹配首先替换,但如果未找到,则替换第二长的匹配。这必须从字符串的开头迭代,否则它不起作用。我正在寻找一种惯用的方法来解决这个问题。

我使用的当前代码有效,但我认为有一种更惯用的方法来解决这个问题。这是我目前拥有的:


s={'11': '++--', '01': '-+-+-', '000': '+-++++++', '001': '--', '100': '--+-', '101': '----++-+--'}

peekstart=0
peekend=1
sptr='110011111000011010110011'
bytestr=b''
while peekstart+peekend <= sptrlen:
  match=False
  while match == False:
    peek = sptr[peekstart:peekstart+peekend] 
    findptr = s.get(peek)
    while findptr == None:
       peek = sptr[peekstart:peekstart+peekend+stream]
       stream=stream+1
       findptr = s.get(peek)
    if stream == 0:
      peekstart=peekstart+peekend+stream
    else:
      peekstart=peekstart+peekend+stream-1
    stream=0
    bytestr+=s.get(peek).encode()
    match = True
print(bytestr)
b'++----++--++--+-++++++-+-+-----++-+---+-+---+-++--'

有没有更惯用的方法来解决这个问题?

回答

使用这里的答案,我想出了这个新版本,它可以很好地解压缩,并且不需要为以零开头的二进制文件提供缓冲区:

from functools import reduce
s={'11': '++--', '01': '-+-+-', '000': '+-++++++', '001': '--', '100': '--+-', '101': '----++-+--'}

def decompressHuffmanCode(a, bit):
    return ('', a[1] + a[2][a[0]+bit[0]], a[2]) if (a[0]+bit[0] in a[2]) else (a[0]+bit[0], a[1], a[2])

# Added one to the front to deal with cases of the binary starting with zeroes
compressed_binary='1110011111000011010110011'
read_compressed_binary = compressed_binary[1:]
remainder,bytestr,s = reduce(decompressHuffmanCode, read_compressed_binary, ('', '', s))
print(bytestr)

感谢大家的回复,他们促成了这个缩短的版本

【问题讨论】:

  • 所以,某些匹配项,例如00 =&gt; '-------------------------------------' 优先于某些其他匹配项000 =&gt; '-' ?
  • 嗯,这是一个霍夫曼代码,所以二进制文件中没有重叠,唯一的问题是它必须从左到右替换,否则会有重叠。我希望有一种惯用的方法可以使用 itertools 或其他东西来解决这个问题,因为我的代码在工作时似乎不必要地迟钝。
  • @oppressionslayer 您帖子中的答案 sn-p 被混淆了,这使得它难以维护。我建议接受下面 RoyM 提供的答案,因为它更易于阅读和维护。
  • 在这些方面这是最好的答案,我选择了它

标签: python string substring


【解决方案1】:

这是一个常见的吃功能。你从左边开始吃,直到找到与你的字典匹配的东西。

text='110011111000011010110011'
s={'11': '++--', '01': '-+-+-', '000': '+-++++++', '001': '--', '100': '--+-', '101': '----++-+--'}

string = ''
i = 0
for j in range(len(text) + 1):
    symbol = s.get(text[i:j])
    if symbol is not None:
        string += symbol
        i = j

print(string)

输出:++----++--++--+-++++++-+-+-----++-+---+-+---+ -++--

【讨论】:

    【解决方案2】:

    我可以想到两种方法来做到这一点,但我不确定备选方案 1 在从左到右的意义上是否正确。最好编写几个测试来检查它的行为是否正确。

    备选方案 1:使用正则表达式

    (现在你有两个问题?:P)

    import re
    def transform(input_string, substitutions):
    
        pattern = "|".join(sorted(substitutions.keys(), key = lambda x: len(x)))
        def replacement(match):
            return substitutions[match.group(0)]
    
        subs = 1
        while subs > 0:
            input_string, subs = re.subn(pattern, replacement, input_string)
        return input_string
    
    
    s = {
        '11': '++--',
        '01': '-+-+-', 
        '000': '+-++++++', 
        '001': '--', 
        '100': '--+-', 
        '101': '----++-+--'
    }
    
    print(transform('110011111000011010110011', s))
    

    这会将所有按长度排序的键与or (|) 连接起来,因此它应该首先替换最短的。然后它只是替换,直到它可以找到更多。它适用于您的情况,但我不确定它如何处理更复杂的情况。

    备选方案 2:使用 functools.reduce

    from functools import reduce
    
    s = {
        '11': '++--',
        '01': '-+-+-', 
        '000': '+-++++++', 
        '001': '--', 
        '100': '--+-', 
        '101': '----++-+--'
    }
    
    def partial_transform(accumulator,bit):
    
        blob, result = accumulator
        next_blob = blob + bit
    
        if next_blob in s.keys():
            return ('', result + s[next_blob])
    
        return (next_blob, result)
    
    
    print(reduce(partial_transform, '110011111000011010110011', ('','')))
    

    这种方法类似于其他答案,因为它从左侧“吃”直到找到匹配项,然后将其添加到结果中,然后继续直到输入结束。

    【讨论】:

      【解决方案3】:

      这是@Artogs's "alternative 2"reduce 方法,但有一点代码golf

      def worker(aggregate, bit):
          prefix, out = aggregate
          key = prefix + bit
          return ('', out + s[key]) if (key in s) else (key, out)
      
      remainder,string = reduce(worker, '110011111000011010110011', ('', ''))
      print string
      

      【讨论】:

      • 这太棒了,让我想知道是否有一个单行来解压缩霍夫曼树,这就是我的代码。我的代码大小减少了 70%,我正在其他代码上对其进行测试,到目前为止一切都很好。
      • 我用这个做了一个 :-) 太棒了!
      【解决方案4】:
      text='110011111000011010110011'  
      s={'11': '++--', '01': '-+-+-', '000': '+-++++++', '001': '--', '100': '--+-', '101': '----++-+--'}
      short_keys = [k for k in s.keys() if len(k) == 2]
      long_keys = [k for k in s.keys() if len(k) == 3]
      
      i=0
      result = ""
      while i <= len(text) -2:
          if text[i:i+2] in short_keys:
              result += s[text[i:i+2]]
              i+=2
          else:
              if i == len(text)-3:
                  break
              result += s[text[i:i+3]]
              i+=3
      

      请注意,如果在字典中找不到合适的键,则会引发键错误。

      【讨论】:

        猜你喜欢
        • 2011-06-30
        • 1970-01-01
        • 2012-09-07
        • 1970-01-01
        • 2019-10-13
        • 1970-01-01
        • 1970-01-01
        • 2012-01-07
        相关资源
        最近更新 更多