【问题标题】:How to check if an element in a list comes from after a given element [duplicate]如何检查列表中的元素是否来自给定元素[重复]
【发布时间】:2019-09-24 11:28:57
【问题描述】:

我有一个列表,想检查列表中是否存在给定序列。
例如,给定序列是'a','h','z',列表是l = ['a','b','h','g','z']。鉴于列表中的 z 在 a 之后的 h 之后,我需要代码返回 True 值。

def check_for_a_h_z(seq):
    return ('a','h','z') in zip(seq, seq[1:], seq[2:])

如果只有 'a','h','z' 紧随其后,则代码返回 true。

【问题讨论】:

  • 我是this answer 的粉丝,来自被称为 dup 目标的那个问题。它适用于字符串、字符列表、字符串列表。

标签: python arrays list sorting sequence


【解决方案1】:

信封蛮力尝试为两个序列提供通用解决方案:

from typing import Sequence

def ordered_contains(s1: Sequence, s2: Sequence) -> bool:
  '''
  >>> ordered_contains('ahz', 'abhgz')
  True
  >>> ordered_contains('', 'asdlkjf')
  True
  >>> ordered_contains('lol', 'lofi')
  False
  '''
  i = 0
  for v in s1:
    try:
      i = s2.index(v, i)
    except ValueError:
      return False
  return True

【讨论】:

  • 请运行一个示例
  • @user10316146 好的,我添加了doctests 以显示您要求的示例。
  • @PatrickArtner 我认为如果 n=len(s1) 和 m=len(s2),那么它就是 Θ(n+m)。如果我错了,请纠正我,我对算法的复杂性很草率。
  • 我想if len(s1) > len(s2): return False 将是一个微不足道的优化,但出于审美原因,我不会把它放进去。 :)
  • @PatrickArtner 最糟糕的情况是,除了 s1 的最后一个元素之外的所有元素都在 s2 中,但最后一个元素不在,对吧?在这种情况下,我们遍历所有 s1 并遍历所有 s2。对于通过 s1 的每一步,我们扫描 s2 的至少一个元素,直到 len(s2)。这就是为什么我仍然认为这是 Θ(n+m) 的最坏情况。
【解决方案2】:

这是一种使用递归的方法:

def list_sequence_recursive(test, lst):
    if len(test) == 1:
        return test[0] in lst
    else:
        if test[0] in lst:
            idx = lst.index(test[0]) + 1  # don't send the matched element
            return list_sequence_recursive(test[1:], lst[idx:])
        else:
            return False

test_sequence_recursive(['a', 'h', 'z'],
                        ['a','b','h','g','z'])
# True

请注意,我们使用lst.index(test[0]) + 1 作为下一次迭代的起始索引,以便我们只发送匹配的元素之后。如果您不使用+ 1,即使您只有一个'a',您也会错误地将输入列表与['a', 'a', 'h'] 匹配。

对于您的示例,这将:

  1. 找到'a',然后用参数['h', 'z']['b','h','g','z']调用它自己
  2. 找到'h',然后用参数['z']['g', 'z']调用它自己
  3. 找到'z' 并返回True 上链

【讨论】:

    【解决方案3】:

    一种有点直观的方法,但不是很 Pythonic:

    l=['a','b','h','g','z']
    
    def check_for_a_h_z(seq):
        if 'a' in seq:
            ind=seq.index('a')
            if 'h' in seq[ind:]:
                ind2=seq.index('h')
                if 'z' in seq[ind2:]:
                    return True
                else:
                    return False
            else:
                return False
        else:
            return False
    
    
    check_for_a_h_z(l)
    

    【讨论】:

      【解决方案4】:

      如果 thingsO(n) 最坏的情况下 seq 是有序的,这将返回 True

      def check_for_things(things,seq):
          k = things[:]
          for c in seq:
              if c == k[0]:
                  k.pop(0)
              if not k:
                  break
      
          return not k  # if you popped all from k you are done
      
      print( check_for_things(list("ahz"), list('abhgz')))
      print( check_for_things(list("ahiz"), list('abhgz')))
      

      输出:

      True  
      False 
      

      它还会为list("ahz"), list('abhhhhhhhhhhgz') 生成True - 多余的h 将被忽略。


      更优化的解决方案(由@Aran-Frey 建议)将使用deque - 在list 末尾的任何地方弹出元素代价高昂,因为整个剩余的list 数据被移动了1 - 双端队列可以O(1) 中的 popleft(和 popright):

      def better_check_for_things(things,seq):
          k = deque(things)
          for c in seq:
              if c == k[0]:
                  k.popleft()
              if not k:
                  break
      
          return not k  # if you popped all from k you are done
      
      print( better_check_for_things(list("ahz"), list('abhgz')))
      print( better_check_for_things(list("ahiz"), list('abhgz')))
      

      【讨论】:

      • 这就是我的想法
      • 如果您要使用k.pop(0),我建议您使用deque 而不是列表。
      • @Aran-Fey 从列表的前面弹出是昂贵的,同意。添加了双端队列版本。
      猜你喜欢
      • 2022-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-06
      • 1970-01-01
      • 2020-04-28
      • 1970-01-01
      • 2017-12-19
      相关资源
      最近更新 更多