我最近有一个面试问题是这样的:
给定一个大字符串(haystack),找到一个子串(针)?
我有点难以找到一个合适的解决方案。
没有时间复杂度差的最佳方法是什么?
答案 0 :(得分:10)
我喜欢Boyer-Moore algorithm。当您在大海捞针中找到大量针头时(例如,电子邮件语料库中的可能垃圾邮件模式),实施起来特别有趣。
答案 1 :(得分:9)
您可以使用the Knuth-Morris-Pratt algorithm,即O(n + m),其中n是“haystack”字符串的长度,m是搜索字符串的长度。
答案 2 :(得分:5)
一般问题是string searching;根据应用程序的性质,有许多算法和方法。
一些高级索引数据结构也用于其他应用程序。 Suffix trees在生物信息学中被大量使用;在这里你有一个长引用文本,然后你有许多任意字符串,你想找到所有出现的。一旦建立了索引(即树),就可以非常有效地找到模式。
对于面试答案,我认为展示广度也更好。了解所有这些不同的算法以及它们最佳服务的具体目的可能比仅仅了解一种算法要好。
答案 3 :(得分:3)
我不相信有任何更好的事情,然后一次看一个干草堆,并试图将它们与针头相匹配。
这将具有线性时间复杂度(与干草堆的长度相对)。如果针与大海捞针的长度接近,并且共享一个长的共同重复前缀,它可能会降级。
答案 4 :(得分:2)
典型的算法如下,字符串索引范围为0到M-1。
返回匹配的位置,如果未找到则返回-1。
foreach hpos in range 0 thru len(haystack)-len(needle):
found = true
foreach npos in range 0 thru len(needle):
if haystack[hpos+npos] != needle[npos]:
found = false
break
if found:
return hpos
return -1
它具有合理的性能,因为它只检查每个干草堆位置中的多个字符,以发现没有匹配的机会。
它不一定是最有效的算法,但如果你的面试官希望你知道所有高性能算法,那么它们就是不现实的(即傻瓜)。一个优秀的开发人员很了解基础知识,以及如何在必要时找到高级内容(例如,当出现性能问题时,而不是之前)。
O(a)和O(ab)之间的性能范围取决于字符串中的实际数据,其中a和b分别是干草堆和针的长度。
一个可能的改进是在npos循环中存储大于hpos的第一个位置,其中字符与针的第一个字符匹配。
这样你可以在下一次迭代中跳过hpos,因为你知道在那之前不可能有匹配。但同样,根据您的性能要求,这可能没有必要。你应该自己在速度和可维护性之间找到平衡点。
答案 5 :(得分:1)
Hacking a Google Interview Practice Questions – Person A中讨论了这个问题。他们的样本解决方案:
bool hasSubstring(const char *str, const char *find) {
if (str[0] == '\0' && find[0] == '\0')
return true;
for(int i = 0; str[i] != '\0'; i++) {
bool foundNonMatch = false;
for(int j = 0; find[j] != '\0'; j++) {
if (str[i + j] != find[j]) {
foundNonMatch = true;
break;
}
}
if (!foundNonMatch)
return true;
}
return false;
}
答案 6 :(得分:0)
这是一个presentation的一些算法(与洪堡大学无耻地联系在一起)。包含一些很好的算法,如Boyer More和Z-box。
我确实使用了Z-Box算法,发现它运行良好并且比Boyer-More更有效率,但需要一些时间来绕过它。
答案 7 :(得分:0)
我认为最简单的方法是“草堆”。包含(“针”);) 很有趣,不要把它当回事。我想你已经得到了答案。