我有一个长文本(大约 5 MB 文件大小)和另一个称为模式的文本(大约 2000 个字符)。
任务是从长文本中 15 个字符或更长的基因组模式中找到匹配的部分。
例子:
长文本: ACGTACGTGTCA AAAACCCCGGGGTTTTA GTACCCGTAGGCGTAT 并且 更长
模式: ACGGTATTGAC AAAACCCCGGGGTTTA TGTTCCCAG
我正在寻找一种高效(且易于理解和实现)的算法。
如果可能的话,奖金将是一种在 C++ 中仅使用字符数组来实现这一点的方法。
这是一种算法——我不确定它是否有名字。它需要一个“滚动散列” - 一个(非加密)散列函数,它具有给定序列散列的属性,AB...C计算序列的散列是有效的B...CD。
计算序列pattern[0..14], pattern[1..15], pattern[2..16]... 的滚动哈希,并将每个索引存储pattern在哈希表中。
计算滚动哈希,haystack[0..14]看看它是否在哈希表中。如果是,则与从哈希表中检索haystack[0..14]到的pattern[pos..pos+14]位置进行比较。pos
从 的滚动哈希中haystack[0..14],高效计算 的滚动哈希,haystack[1..15]看是否在哈希表中。重复直到到达终点haystack。
请注意,您的 15 个字符串只有 2 30 个可能的值,因此您的“哈希函数”可能是一个简单的映射到字符串的值,该字符串被视为 15 位 base-4 数字,计算速度很快,具有滚动哈希属性并且是独一无二的。
一种方法是获取Aho-Corasick的实现并使用它来创建可以识别模式中任何 15 个字符块的内容,然后使用它来搜索文本。使用 Aho-Corasick 构建匹配器的成本和搜索成本都是线性的,所以这应该是实用的。
退后一步,我要现场编码:
void match_substring(const char *a, const char *b, int n) // n=15 in your case
{
int alen = strlen(a); // I'll leave all the null-checking and buffer-overrun business as an exercise to the reader
int blen = strlen(b);
for (int i=0; i<alen; i++) {
for (int j=0; j<blen; j++) {
for (int k; (i+k<alen) && (j+k<blen) && a[i+k]==b[i+k]; k++);
if (k >= n)
printf("match from (%d:%d) for %d bytes\n", i, j, k);
}
}
}
如果您使用的是 C 库的良好实现(或者甚至是像 glibc 这样的平庸的库,恰好具有该函数的良好实现),strstr那么会做得很好。我听说有一种新算法对 DNA(小字母)特别有用,但我现在找不到参考。除此之外,2way(glibc 使用)是最佳的。
我强烈建议你去你的图书馆看看 Robert Sedgwick 和 Kevin Wayne 的“Algorithms 4th Edition”。他们有一整章专门讨论子字符串搜索。此外,可能值得查看书籍网站algs4.cs.princeton.edu。
TL;DR——如果你下定决心,你可以在保证时间与输入长度成线性关系时使用 char 数组进行子字符串搜索。书中和网上都有代码示例。没有比这更容易的了。
我认为“后缀树”可以通过 O(log n) 的性能更好地解决它