Algorithm 检查字符串是否由子字符串列表生成的算法

Algorithm 检查字符串是否由子字符串列表生成的算法,algorithm,string,substring,Algorithm,String,Substring,您将获得一个字符串和一个字符串数组。如何快速检查是否可以通过连接数组中的一些字符串来构建此字符串 这是一个理论问题,出于实际原因,我不需要它。但我想知道,是否有一些好的算法 编辑 阅读一些答案,我注意到,这可能是NP完全问题。即使找到字符串的子集,它们的长度也会相同,因为给定的字符串是一个经典的子集和问题 所以我想这个问题没有简单的答案 编辑 现在看来,这毕竟不是一个NP完全问题。这就更酷了:-) 编辑 我提出了一个通过一些测试的解决方案: def can_build_from_substrin

您将获得一个字符串和一个字符串数组。如何快速检查是否可以通过连接数组中的一些字符串来构建此字符串

这是一个理论问题,出于实际原因,我不需要它。但我想知道,是否有一些好的算法

编辑 阅读一些答案,我注意到,这可能是NP完全问题。即使找到字符串的子集,它们的长度也会相同,因为给定的字符串是一个经典的子集和问题

所以我想这个问题没有简单的答案

编辑

现在看来,这毕竟不是一个NP完全问题。这就更酷了:-)

编辑

我提出了一个通过一些测试的解决方案:

def can_build_from_substrings(string, substrings):
    prefixes = [True] + [False] * (len(string) - 1)
    while True:
        old = list(prefixes)
        for s in substrings:
            for index, is_set in enumerate(prefixes):
                if is_set and string[index:].startswith(s):
                    if string[index:] == s:
                        return True
                    prefixes[index + len(s)] = True
        if old == prefixes: # nothing has changed in this iteration
            return False

我相信时间是
O(n*m^3)
,其中
n
子字符串的长度
m
字符串的长度
。你觉得怎么样?

我会这样做的

  • 确定目标字符串的长度
  • 确定子字符串数组中每个字符串的长度
  • 确定哪个子字符串组合将生成与目标字符串长度相同的字符串(如果有,如果没有,则完成)
  • 生成步骤3中确定的子串组合的所有置换。检查它们是否与目标字符串匹配

  • 生成所有排列是一项处理器繁重的任务,因此如果您可以减少“n”(输入大小),您将获得相当大的效率。

    这肯定不快,但您有一个想法:

    • 迭代所有字符串,检查目标字符串是否以其中任何一个“开始”
    • 取目标字符串开头的最长字符串,将其从列表中删除,并将其从主字符串中修剪
    • 冲洗,重复
    当剩下长度为0的目标字符串时停止

    正如我之前所说,这肯定不是很快,但应该给你一个基线(“它不应该变得比这更糟糕”)

    编辑

    正如评论中指出的那样,这是行不通的。您将不得不存储部分匹配项,当您发现没有进一步的方法时,您将依赖它们

    • 当您发现字符串是目标的头部时,将其推到列表中。建立列表后,您自然会尝试目标中最大的“头”
    • 当你发现你试过的头与剩下的不匹配时,试试下一个最好的头

    这样,最终您将探索解决方案的整个空间。对于每一个候选人的头部,你都会尝试每一个可能的尾部。

    两个选项在脑海中闪现,但它们看起来都不是很优雅

    1) 蛮力:像密码生成器一样操作,如word1+word1+word1>word1+word1+word2>word1+word1+word3等

    诀窍在于长度,因此你必须尝试2个或更多单词的所有组合,而你不知道在哪里设置限制。非常耗时


    2) 取有问题的字符串,对每个单词一次查找1个。可能会检查长度,如果大于0,请再次检查。继续这样做,直到你达到零它找不到任何更多的结果。如果你打0,那就是赢,如果不是输的话。我认为这种方法比第一种要好得多,但我想有人会有更好的建议。

    在我看来,一个问题可以通过简单的数组线性遍历和比较来解决。但是,可能会有多次通过。您可以设计一种策略来最小化传递。例如,在第一个过程中构造原始字符串的所有子字符串的子数组。然后线性地尝试不同的变化。

    这里有一个大概的想法应该是可行的

  • 将源字符串复制到新字符串中
  • 当复制字符串仍有数据且仍有子字符串时 A.如果copy.contains(substr)copy.remove(substr),则获取子字符串
  • 如果副本现在是空的,那么是的,您可以构造字符串
  • 如果copy不是空的,抛出从字符串中删除的第一个子字符串并重复
  • 如果所有的子字符串都消失了,并且copy仍然不是空的,那么就不能构造它
  • 编辑:
    一种可能的改进方法是首先迭代所有子字符串,并抛出主字符串中不包含的任何子字符串。然后完成上述步骤。

    注意:我假设您可以多次使用每个子字符串。您可以通过更改子问题的定义方式,将解决方案概括为包含此限制。这将对空间和预期运行时间产生负面影响,但问题仍然存在。

    这是一个动态规划问题。(这是一个很好的问题!)

    如果可以使用子字符串列表
    W
    编写字符串
    s
    ,那么让我们将
    composable(s,W)
    定义为true

    S
    在以下情况下才可组合:

  • S
    w
    中的子字符串
    w
    开头
  • w
    之后的
    S
    的其余部分也是可组合的
  • 让我们编写一些伪代码:

    COMPOSABLE(S, W):
      return TRUE if S = "" # Base case
      return memo[S] if memo[S]
    
      memo[S] = false
    
      for w in W:
        length <- LENGTH(w)
        start  <- S[1..length]
        rest   <- S[length+1..-1]
        if start = w AND COMPOSABLE(rest, W) :
          memo[S] = true # Memoize
    
      return memo[S]
    

    受@cnicutars启发,答案:

    • 函数
      可能(数组A、字符串s)
      • 如果
        s
        为空,则返回true
      • 计算
        A
        中作为
        s
        前缀的所有字符串的数组
        P
      • 如果
        P
        为空,则返回false
      • 对于
        p
        中的每个字符串
        p
        • 如果
          可能(A去掉p,s去掉前缀p)
          返回true
      • 返回错误

    如果每个子字符串只能使用一次,而不是所有子字符串都必须使用

    对于与原始字符串大小相等的子字符串中大小为N的每个排列,请检查它,如果没有,则进行排列
    Find all z occurrences of the patterns P1..Pn of total length m
    enter code hereas substrings in O(m + z) time.