字符串平铺算法

时间:2009-09-17 20:41:42

标签: algorithm string tiling

我正在寻找一种有效的算法来进行字符串平铺。基本上,您会收到一个字符串列表,例如 BCD CDE ABC A ,生成的平铺字符串应为 ABCDE ,因为BCD与{CDE对齐1}}产生BCDE,然后与ABC对齐,产生最终的ABCDE

目前,我正在使用一种稍微天真的算法,其工作原理如下。从一对随机字符串开始,比如BCDCDE,我使用以下(在Java中):

public static String tile(String first, String second) {
  for (int i = 0; i < first.length() || i < second.length(); i++) {
    // "right" tile (e.g., "BCD" and "CDE")
    String firstTile = first.substring(i);
    // "left" tile (e.g., "CDE" and "BCD")  
    String secondTile = second.substring(i);
    if (second.contains(firstTile)) {
      return first.substring(0, i) + second;
    } else if (first.contains(secondTile)) {
      return second.substring(0, i) + first;
    }
  }
  return EMPTY;
}

System.out.println(tile("CDE", "ABCDEF")); // ABCDEF
System.out.println(tile("BCD", "CDE")); // BCDE
System.out.println(tile("CDE", "ABC")); // ABCDE
System.out.println(tile("ABC", tile("BCX", "XYZ"))); // ABCXYZ

虽然这样做有效,但效率并不高,因为它会一遍又一遍地重复相同的字符。

那么,是否有人知道更好(更有效)的算法?这个问题类似于DNA序列比对问题,因此非常欢迎来自该领域的人(以及其他人)的任何建议。另请注意,我不是在寻找对齐,而是在寻找平铺,因为我要求其中一个字符串与另一个字符串完全重叠。

我目前正在寻找Rabin-Karp algorithm的改编版,以提高算法的渐近复杂度,但我想在深入研究这个问题之前先听一些建议。

提前致谢。


对于存在歧义的情况 - 例如,{ABC, CBA}可能导致ABCBACBABC - ,可以返回任何平铺。但是,这种情况很少发生,因为我正在拼写单词,例如{This is, is me} => {This is me}被操纵,以便上述算法有效。

类似的问题:Efficient Algorithm for String Concatenation with Overlap

5 个答案:

答案 0 :(得分:4)

按照第一个字符,然后是长度(从最小到最大)对字符串进行排序,然后将自适应应用于this question中关于连接重叠字符串的KMP。

答案 1 :(得分:2)

我认为这应该适用于两个字符串的平铺,并且比使用substring和contains的当前实现更有效。从概念上讲,我遍历'left'字符串中的字符,并将它们与'right'字符串中的字符进行比较。如果两个字符匹配,我将移动到右侧字符串中的下一个字符。根据首次到达末尾的字符串,以及最后比较的字符是否匹配,可以识别其中一个可能的平铺案例。

我没有想过要改善拼贴两个以上字符串的时间复杂度。作为多个字符串的小注释,下面的这个算法很容易扩展到一次检查单个“左”字符串的平铺,多个“正确”字符串,这可能会阻止字符串的额外循环,如果你试图通过尝试所有可能性,找出是做(“ABC”,“BCX”,“XYZ”)还是(“ABC”,“XYZ”,“BCX”)。有点。

string Tile(string a, string b)
{
    // Try both orderings of a and b,
    // since TileLeftToRight is not commutative.

    string ab = TileLeftToRight(a, b);

    if (ab != "")
        return ab;

    return TileLeftToRight(b, a);

    // Alternatively you could return whichever
    // of the two results is longest, for cases
    // like ("ABC" "BCABC").
}

string TileLeftToRight(string left, string right)
{
    int i = 0;
    int j = 0;

    while (true)
    {
        if (left[i] != right[j])
        {
            i++;

            if (i >= left.Length)
                return "";
        }
        else
        {
            i++;
            j++;

            if (i >= left.Length)
                return left + right.Substring(j);

            if (j >= right.Length)
                return left;
        }
    }
}

答案 2 :(得分:1)

如果开源代码是可以接受的,那么你应该检查斯坦福大学STAMP基准测试套件中的基因组基准测试:它几乎完全符合你的需求。从一堆字符串(“基因”)开始,它寻找包含所有基因的最短字符串。例如,如果您有ATGC和GCAA,它会找到ATGCAA。算法没有任何限制它将其限制为4个字符的字母表,所以这应该能够帮助你。

答案 3 :(得分:0)

首先要问的是,您是否想要找到{CDB,CDA}的耕种?没有单一耕种。

答案 4 :(得分:0)

有趣的问题。你需要某种回溯。例如,如果你有:

ABC, BCD, DBC 

将DBC与BCD结合起来:

ABC, DBCD

哪个不可解决。但是将ABC与BCD结合起来可以得到:

ABCD,DBC

可以合并到:

ABCDBC.