寻找子串的长度

时间:2013-05-09 07:02:47

标签: string algorithm search substring

我给了n个字符串。我必须找到一个字符串S,因此,给定n个字符串是S的子序列。  例如,我给出了以下5个字符串:

    AATT
    CGTT
    CAGT
    ACGT
    ATGC

然后字符串是“ACAGTGCT”。 。因为,ACAGTGCT包含所有给定的字符串作为超级序列。 要解决这个问题,我必须知道算法。但我不知道如何解决这个问题。伙计们,你能告诉我解决这个问题的技巧吗?

3 个答案:

答案 0 :(得分:0)

我的方法:使用Trie

Building a Trie from the given words.
create empty string (S)
create empty string (prev)

for each layer in the trie
    create empty string (curr)
    for each character used in the current layer
        if the character not used in the previous layer (not in prev)
            add the character to S
            add the character to curr
    prev = curr

希望这会有所帮助:)

答案 1 :(得分:0)

这是一个NP完全问题,称为多序列比对。

wiki page描述了解决方法,例如动态编程,它适用于小n,但对于较大的n来说变得非常昂贵。

基本思想是构造一个数组f [a,b,c,...],表示产生第一个字符串的“a”字符的最短字符串S的长度,第二个字符串的“b”字符,和第三个“c”字符。

答案 2 :(得分:0)

1 Definitions

长度为n的序列是取自字母表的n个符号的串联。     如果S是长度为n的序列,并且T是长度为m和n m的序列,则如果可以通过从T中删除m-n个符号来获得S,则S是T的子序列。符号不需要是连续的。     如果可以通过插入m-n个符号来获得T,则长度为m的序列T是长度为n的S的超序列。也就是说,当且仅当S是T的子序列时,T是S的超序。     序列T是序列S1的常见超序列,并且T的S2是S1和S2的超序列。

2 The problem

问题是找到最短的公共超序列(SCS),这是最小长度的常见超序。对于给定的问题,可能存在多个SCS。

2.1 Example


S= {a, b, c}
S1 = bcb
S2 = baab
S3 = babc

一个最短的常见超级序列是babcab(babacb,baabcb,bcaabc,bacabc,baacbc)。

3 Techniques

动态编程除非输入序列的数量非常小,否则需要太多内存。 分支和界限除非字母表非常小,否则需要太多时间。 多数合并当序列数量大于字母大小时,最着名的启发式算法。 [1] 贪婪(取两个序列并用最佳的最短公共超序替换它们直到留下一个字符串)比多数合并更糟糕。 [1] 遗传算法表明它可能比多数合并更好。 [1]

4 Implemented heuristics

4.1 The trivial solution

平凡的解决方案最多是||乘以最优解长度,并通过将sigma中所有字符的串联连接为最长序列的次数来获得。也就是说,如果= {a,b,c}且最长的输入序列长度为4,我们得到abcabcabcabc。 4.2多数合并启发式 Majority合并启发式按以下方式从空序列(S)构建超级序列:

尽管有非空的输入序列     s< - 非空输入序列开始时最常见的符号。     将s添加到S的末尾。     从以s开头的每个输入序列的开头删除s。   

结束

当序列数量大于字母大小时,多数合并表现得非常好。

5 My approach - Local search

我的方法是将局部搜索启发式应用于SCS问题,并将其与Majority合并启发式进行比较,以确定在字母大小大于序列数的情况下它是否会做得更好。

由于有效超级序列的长度可能会有所不同,并且对超级序列的任何更改都可能会产生无效字符串,因此直接表示超级序列作为可行解决方案不是一种选择。

我选择将可行解(S)视为映射序列x1 ... xSl,其中S1是所有序列长度的总和,xi是序列号和索引的映射。

这意味着,如果L = {{s1,1 ... s1,m1},{s2,1 ... s2,m2} ... {sn,1 ... s3,mn}}是输入序列集和L(i)是第i个序列,映射表示如下:

xi {k,l},其中k L和l L(k)

为确保任何解决方案都有效,我们需要引入以下约束:

  1. 每个序列中的每个符号可能只有一个xi映射到它。
  2. 如果xi ss,k和xj ss,则l和k <1。然后我&lt;学家
  3. 如果xi ss,k和xj ss,l和k&gt;然后我&gt;学家
  4. 第二个约束强制保留每个序列的顺序,但不保留它在S中的位置。如果我们有两个映射xi和xj,那么我们只能在映射到不同序列时交换映射之间的映射。

    5.1 The initial solution
    

    选择初始解决方案的方法有很多种。只要保留序列的顺序,它就是有效的。我选择不以某种方式随机化解决方案,但尝试两种截然不同的解决方案类型并进行比较。

    第一个是通过简单连接所有序列来创建初始解决方案。

    第二个是一次将序列交错一个符号。那就是从每个序列的第一个符号开始,然后按照相同的顺序,取每个序列的第二个符号,依此类推。

    5.2 Local change and the neighbourhood
    

    通过在解决方案中交换两个映射来完成本地更改。 进行迭代的一种方法是从i到S1并为每个映射进行最佳交换。 另一种方法是尝试按照序列定义的顺序交换映射。也就是说,首先交换s1,1,然后是s2,1。这就是我们所做的。

    我尝试过两种变体。

    在第一个中,如果单个映射交换没有产生更好的值,我会返回,否则我继续。

    在第二个中,我单独为每个序列进行与序列一样多的交换,因此每个序列中的符号将有可能移动。交换提供了我保留的最佳价值,如果该值比算法中最后一步的值差,我会返回,否则我继续。

    只要交换不改变原始序列的顺序,符号就可以向左或向右移动任意数量的位置。

    第一个变体中的邻域是可以为符号进行的有效交换的数量。在第二个变体中,它是在交换前一个符号之后每个符号的有效交换的总和。

    5.3 Evaluation
    

    由于溶液的长度始终是恒定的,因此必须先进行压缩,然后才能获得溶液的实际长度。

    通过使用每个映射指向的符号将由映射组成的解S转换为字符串。创建一个新的,初始化为空的解决方案T.然后执行该算法:

    T = {}
      FOR i = 0 TO Sl
        found = FALSE
        FOR j = 0 TO |L|
           IF first symbol in L(j) = the symbol xi maps to THEN
              Remove first symbol from L(j)
              found = TRUE
           END IF
        END FOR 
        IF found = TRUE THEN  
          Add the symbol xi maps to to the end of T            
        END IF
      END FOR
    

    S1与所有序列的长度之和一样。 L是所有序列的集合,L(j)是序列号j。

    获得溶液S的值作为| T |。

    很多人都感谢:Andreas Westling