Algorithm 在字符串中查找字符串的典型算法是什么?
我最近有一个面试问题是这样的: 给定一个大字符串(草堆),找到一个子字符串(针) 我有点为难,没能想出一个像样的解决办法Algorithm 在字符串中查找字符串的典型算法是什么?,algorithm,language-agnostic,Algorithm,Language Agnostic,我最近有一个面试问题是这样的: 给定一个大字符串(草堆),找到一个子字符串(针) 我有点为难,没能想出一个像样的解决办法 在时间复杂度不差的情况下,实现这一点的最佳方法是什么?您可以使用,即O(n+m),其中n是“干草堆”的长度string和m是搜索字符串的长度。我不认为有什么比一次只看一个字符,然后尝试将它们与指针匹配更好的了 这将具有线性时间复杂性(相对于干草堆的长度)。如果指针的长度与干草堆的长度接近,并且共享一个长的公共前缀和重复前缀,则指针的性能可能会降低。中讨论了此问题。他们的样品溶
在时间复杂度不差的情况下,实现这一点的最佳方法是什么?您可以使用,即O(n+m),其中n是“干草堆”的长度string和m是搜索字符串的长度。我不认为有什么比一次只看一个字符,然后尝试将它们与指针匹配更好的了
这将具有线性时间复杂性(相对于干草堆的长度)。如果指针的长度与干草堆的长度接近,并且共享一个长的公共前缀和重复前缀,则指针的性能可能会降低。中讨论了此问题。他们的样品溶液:
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;
}
我喜欢这个。当你在大海捞针时(例如,电子邮件语料库中可能存在的垃圾邮件模式),实现它尤其有趣。典型的算法如下,字符串索引范围从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的第一个位置,其中字符与针的第一个字符匹配
这样,您可以在下一次迭代中向前跳过HPO,因为您知道在该点之前不可能存在匹配。但是,根据您的性能要求,这可能不是必需的。你应该自己在速度和可维护性之间找到平衡。一般的问题是;根据应用程序的性质,有许多算法和方法
- ,在另一个字符串中搜索一个字符串
- ,字符串搜索中的另一个字符串
- );在给定的任意文本中从参考词典中搜索单词
对于面试的回答,我认为最好也表现出广度。了解所有这些不同的算法以及它们最适合的具体用途可能比只记住一个算法要好。以下是一些算法的例子(毫不羞耻地链接自洪堡大学)。包含一些好的算法,如Boyer More和Z-box
我确实使用了这个算法,发现它运行得很好,比Boyer更有效,但是需要一些时间来思考它。我认为最简单的方法是“干草堆”。包含(“针”);)
只是好玩,别当真。我想你已经得到答案了 James McNellis提到的Knuth-Morris-Pratt算法是对该算法的改进,允许在匹配失败后跳过几个字符。但仍然是O(n)。记住这是一个搜索,而不是一个比较。考虑在具有重复字符的字符串中搜索具有许多重复字符的字符串,并且您将意识到您的算法实际上是O(nm)最坏的情况,其中n和m都是一个字符串的长度。两个变量的乘积不是线性的(它不是一个未知常数乘以一个变量的乘积),当然也不如这两个变量之和那么好,这是可以实现的。@Steve314:这就是我所说的“如果针的长度接近干草堆,并且有一个很长的共同重复前缀,那么就降级”的意思(不过前缀应该是子字符串)。请注意,简单的字符串搜索在实践中通常是相当不错的,因为大多数文本没有导致问题的模式。您基本上是在“大海捞针”中的每个偏移量上进行比较,并且大多数比较不匹配都是在第一个或第二个字符测试中检测到的。@Thilo-好的-我应该意识到。返回匹配的开始索引会很好。在传统的干草堆场景中,您已经知道针在那里。某处。:-)在我看到这一点时键入了一半答案,请阅读文章,意识到这是同一件事。以前从未听说过,但我在94年的二进制模式匹配状态机中使用了类似的东西。哦,好吧。在这种情况下,
从一个内循环继续
一个外循环的能力会很棒。@Mike:“goto被认为很棒”?;-)我认为还有带标签的break/contine。没有必要走极端。@Steve:我想到了,但实际上可能无法做到这一点。在C++术语中,如果setjmp
/longjmp
风格的goto
将是不好的,但是一个抛出并捕获的异常将是好的,如果不是因为它的开销。我不知道如果代码> Goto < /Code >在C++中被清除,因为我从来没有使用过它(在代码审查中太难防御)。这在C语言中是不存在的,但这是因为C语言缺少析构函数,所以有些语言没有