【问题标题】:check if letters of a string are in sequential order in another string检查一个字符串的字母在另一个字符串中是否按顺序排列
【发布时间】:2019-04-17 08:11:05
【问题描述】:

如果只是检查 test_string 中的字母是否也在 control_string 中,

我不会有这个问题。

我将简单地使用下面的代码。

if set(test_string.lower()) <= set(control_string.lower()):
    return True

但我也面临着一个相当复杂的任务,即辨别

中的重叠字母是否

control_string 与 test_string 的顺序相同。

例如,

test_string = 'Dih'
control_string = 'Danish'
True

test_string = 'Tbl'
control_string = 'Bottle'
False

我曾想过使用 for 迭代器来比较字母表的索引,但很难想出合适的算法。

for i in test_string.lower():
    for j in control_string.lower():
        if i==j:
            index_factor = control_string.index(j)

我的计划是将主索引因子与下一个因子进行比较,如果主索引因子大于另一个,则函数返回 False。

我被困在如何在 for 循环中比较那些 index_factors。

我应该如何解决这个问题?

【问题讨论】:

    标签: python string python-3.x python-2.7 for-loop


    【解决方案1】:

    这是一种解决方案。这个想法是遍历 control 字符串 first 并在它匹配下一个 test 字符时产生一个值。如果匹配的总数等于test的长度,那么你的条件就满足了。

    def yield_in_order(x, y):
        iterstr = iter(x)
        current = next(iterstr)
        for i in y:
            if i == current:
                yield i
                current = next(iterstr)
    
    def checker(test, control):
        x = test.lower()
        return sum(1 for _ in zip(x, yield_in_order(x, control.lower()))) == len(x)
    
    test1, control1 = 'Tbl', 'Bottle'
    test2, control2 = 'Dih', 'Danish'
    
    print(checker(test1, control1))  # False
    print(checker(test2, control2))  # True
    

    @tobias_k's answer 有更简洁的版本。如果您想要一些其他信息,例如多少多少个字母在找到中断之前对齐,您可以简单地调整checker 函数以返回sum(1 for _ in zip(x, yield_in_order(...)))

    【讨论】:

    • 这实际上接近我的第二个解决方案(没有看到它),但似乎过于复杂。为什么yield ij 比较?你已经知道他们是平等的。你不能只检查产生的元素数量吗?
    • @tobias_k,好点。我确实尝试了sum(1 for _ in yield_in_order(x, control.lower())) == len(x),但我得到了DeprecationWarning: generator 'yield_in_order' raised StopIteration [我不明白],即使结果是正确的。
    • 我猜想在iterstr 用尽之后尝试匹配更多字符时就是这种情况。使用x 压缩会限制生成器请求的项目数。所以zip 他们是有道理的,但== 仍然是多余的。
    • @tobias_k,是的,花了一点时间但想通了.. 可以sum(1 for ...)。不过,您的解决方案更清晰。
    【解决方案2】:

    您可以只将join 字符串中的test 字符转换为regular expression,允许介于两者之间的任何其他字符.*,然后re.searchcontrol 字符串中使用该模式。

    >>> test, control = "Dih", "Danish"
    >>> re.search('.*'.join(test), control) is not None
    True
    >>> test, control = "Tbl", "Bottle"
    >>> re.search('.*'.join(test), control) is not None
    False
    

    不使用正则表达式,您可以从control 字符串创建一个iter,并使用两个嵌套循环,1)break来自内部循环,else 返回@ 987654333@ 直到test 中的所有字符都在control 中找到。即使control 已经是可迭代的,创建iter 也很重要,这样内部循环将在上次停止的地方继续。

    def check(test, control):
        it = iter(control)
        for a in test:
            for b in it:
                if a == b:
                    break
            else:
                return False
        return True
    

    您甚至可以使用allany 在一行(嗯,两行)中完成此操作:

    def check(test, control):
        it = iter(control)
        return all(any(a == b for b in it) for a in test)
    

    这两种方法的复杂度都应该是 O(n),其中 n 是最大字符数。

    1) 这在概念上类似于 @jpp 所做的,但恕我直言更清楚一点。

    【讨论】:

      【解决方案3】:

      一种简单的方法是使用sorted 中的key 参数,它用作排序比较的键:

      def seq_order(l1, l2):
          intersection = ''.join(sorted(set(l1) & set(l2), key = l2.index))
          return True if intersection == l1 else False
      

      因此,这是计算两个集合的交集并根据较长的字符串对其进行排序。完成后,您只需将结果与较短的字符串进行比较,看看它们是否相同。

      函数相应地返回 True 或 False。使用您的示例:

      seq_order('Dih', 'Danish')
      #True
      
      seq_order('Tbl', 'Bottle')
      #False
      
      seq_order('alp','apple')
      #False
      

      【讨论】:

        【解决方案4】:

        您可以使用find(letter, last_index) 来查找处理过的字母后出现的所需字母。

        def same_order_in(test, control):
            index = 0
            control = control.lower()
            for i in test.lower():
                index = control.find(i, index)
                if index == -1:
                    return False
                # index += 1 # uncomment to check multiple occurrences of same letter in test string  
            return True
        

        如果测试字符串有重复的字母,例如:

        test_string = 'Diih'
        control_string = 'Danish'
        

        带注释行same_order_in(test_string, control_string) == True

        并且带有未注释的行same_order_in(test_string, control_string) == False

        【讨论】:

          【解决方案5】:

          使用生成器的优雅解决方案:

          def foo(test_string, control_string):
              if all(c in control_string for c in test_string):
                  gen = (char for char in control_string if char in test_string)
                  if all(x == test_string[i] for i, x in enumerate(gen)):
                      return True
              return False
          
          print(foo('Dzn','Dahis')) # False
          print(foo('Dsi','Dahis')) # False
          print(foo('Dis','Dahis')) # True
          

          首先检查test_string中的所有字母是否都包含在control_string中。然后检查该订单是否与test_string订单相似。

          【讨论】:

          • 为什么函数会返回 ('Ce', 'Arsenic') 为 True?它不应该返回 false,因为 order 是相反的 (ec)?
          • 你测试过这个吗?它实际上返回 False。
          • 确实返回 False!我想我已经运行了之前的执行。
          • 此解决方案的一个问题是重复的if _ in test_string.. 可能使用set 使其成为O(1)?
          • 你的意思是set
          【解决方案6】:

          递归是解决此类问题的最佳方法。 这是一个检查顺序的方法。

          def sequentialOrder(test_string, control_string, len1, len2): 
          
              if len1 == 0:     # base case 1
                  return True
          
              if len2 == 0:     # base case 2
                  return False
          
              if test_string[len1 - 1] == control_string[len2 - 1]: 
                  return sequentialOrder(test_string, control_string, len1 - 1, len2 - 1)  # Recursion 
          
              return sequentialOrder(test_string, control_string, len1, len2-1)
          
          test_string = 'Dih'
          control_string = 'Danish'
          
          print(isSubSequence(test_string, control_string, len(test_string), len(control_string)))
          

          输出:

          True
          

          False

          test_string = 'Tbl'
          control_string = 'Bottle'
          

          这是一个做同样事情的迭代方法,

          def sequentialOrder(test_string,control_string,len1,len2): 
          
              i = 0
              j = 0
          
              while j < len1 and i < len2: 
                  if test_string[j] == control_string[i]:     
                      j = j + 1    
                  i = i + 1
          
              return j==len1 
          
          test_string = 'Dih'
          control_string = 'Danish'
          
          print(sequentialOrder(test_string,control_string,len(test_string) ,len(control_string)))
          

          【讨论】:

          • 您介意详细说明 len1 和 len2 的功能吗?我刚开始学习python,对递归不熟悉。
          • 递归并非特定于 python。它们是一种方法。我也会在一分钟内以迭代的方式编写它。
          • @VAnon 更新了我的答案。
          猜你喜欢
          • 1970-01-01
          • 2012-11-19
          • 2022-07-01
          • 2012-01-31
          • 1970-01-01
          • 2015-09-06
          • 2019-05-11
          • 2020-07-19
          相关资源
          最近更新 更多