我正在寻找一种有效的算法来进行字符串平铺。基本上,您会收到一个字符串列表,例如 BCD
, CDE
, ABC
, A
,生成的平铺字符串应为 ABCDE
,因为BCD
与{CDE
对齐1}}产生BCDE
,然后与ABC
对齐,产生最终的ABCDE
。
目前,我正在使用一种稍微天真的算法,其工作原理如下。从一对随机字符串开始,比如BCD
和CDE
,我使用以下(在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}
可能导致ABCBA
或CBABC
- ,可以返回任何平铺。但是,这种情况很少发生,因为我正在拼写单词,例如{This is, is me} => {This is me}
被操纵,以便上述算法有效。
类似的问题:Efficient Algorithm for String Concatenation with Overlap
答案 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.