我有一个 haystack字符串,我想检查它是否包含任何 needle字符串。目前我这样做:
Set<String> needles = ...;
...
String [] pieces = haystack.split(" ");
for (String piece: pieces) {
if (needles.contains(piece) {
return true;
}
}
return false;
它有效,但速度相对较慢。
问题:是否有更快的方法来完成任务?
示例
Haystack: I am a big tasty potato .
Needles: big, tasty
== RUN ==
I am a big tasty potato .
|
[tasty] got a match, we are good!
答案 0 :(得分:4)
您应该查看Aho-Corasick算法。这适合您的问题,因为它构建了所有单词(针)的自动机,并在构建的自动机上遍历文本(haystack)以查找所有匹配的单词。它基本上构造了一个类似于trie的有限状态机。
时间复杂度为<div class="container">
<div class="row">
<div class="item col-lg-4 col-md-6 col-sm-6 col-xs-12">...Content</div>
<div class="item col-lg-4 col-md-6 col-sm-6 col-xs-12">...Content</div>
<div class="item col-lg-4 col-md-6 col-sm-6 col-xs-12">...Content</div>
</div>
<div class="row">
<div class="item col-lg-4 col-md-6 col-sm-6 col-xs-12">...Content</div>
<div class="item col-lg-4 col-md-6 col-sm-6 col-xs-12">...Content</div>
<div class="item col-lg-4 col-md-6 col-sm-6 col-xs-12">...Content</div>
</div>
</div>
O(n + m + z)
是文字中出现的字词总数,z
是文字的长度,n
是所有字词中的总字符数。
修改2
这是一个直接的实现,在找到任何针的第一次出现后停止遍历。
m
但是,目前此代码将在文本中找到任何事件的结束位置。如果您需要起始位置和/或针,您可以从结束位置追溯,直到找到一个空格来获得匹配的单词。
在最坏的情况下,这并不能保证速度,但在平均和最佳情况下应该更好。
答案 1 :(得分:1)
通常,您的减速大部分都是split命令。搜索你所拥有的一个字符串比分配一堆垃圾更好。你最好做正则表达式,避免新的对象构造。使用Aho会非常有效。假设你的名单很大,很麻烦。
public class NeedleFinder {
static final int RANGEPERMITTED = 26;
NeedleFinder next[];
public NeedleFinder() {
}
public NeedleFinder(String haystack) {
buildHaystack(haystack);
}
public void buildHaystack(String haystack) {
buildHaystack(this,haystack,0);
}
public void buildHaystack(NeedleFinder node, String haystack, int pos) {
if (pos >= haystack.length()) return;
char digit = (char) (haystack.charAt(pos) % RANGEPERMITTED);
if (digit == ' ') {
buildHaystack(this,haystack,pos+1);
return;
}
if (node.next == null) node.next = new NeedleFinder[RANGEPERMITTED];
if (node.next[digit] == null) node.next[digit] = new NeedleFinder();
NeedleFinder nodeNext = node.next[digit];
buildHaystack(nodeNext,haystack,pos+1);
}
public boolean findNeedle(String needle) {
return findNeedle(this, needle,0);
}
private boolean findNeedle(NeedleFinder node, String needle, int pos) {
if (pos >= needle.length()) return true;
char digit = (char) (needle.charAt(pos) % RANGEPERMITTED);
if (node.next == null) return false;
if (node.next[digit] == null) return false;
return findNeedle(node.next[digit],needle,pos+1);
}
}
成功时,检查包含以确保它不是误报。但是,它很快。我们说的是二分之一搜索速度的1/5。
说到,二分搜索是一个好主意。只有合适的时间复杂性。只需对你的干草堆字符串进行排序,然后当你通过针头进行二元搜索时。在java中,这些是集合中的基本和项目。 .sort()和.binarySearch()命令。并且它将比野兽好几个数量级。
value = Collections.binarySearch(haystackList, needle, strcomp);
如果价值为正,则会找到。
Collections.sort(words, strcomp);
使用strcomp。
public Comparator<String> strcomp = new Comparator<String>() {
@Override
public int compare(String s, String t1) {
if ((s == null) && (t1 == null)) return 0;
if (s == null) return 1;
if (t1 == null) return -1;
return s.compareTo(t1);
}
};
答案 2 :(得分:1)
您可以使用带有parallel
功能
anymatch
流的java8 plus
boolean hi=Arrays.stream(pieces).parallel().anyMatch(i->needle.contains(i));
答案 3 :(得分:1)
你应该确保needless
是一个HashSet
的实例,它包含一个“快速”的恒定时间操作。接下来,如果您不需要,请不要处理所有haystack
...尝试此操作:
int i, j, l = haystack.length();
for(i = 0; i < l; i = j + 1) {
j = haystack.indexOf(' ', i + 1);
if(j == -1) {
j = l - 1;
}
String hay = haystack.s substring(i, j - 1).trim();
if(hay.length() > 0 && needles.contains(hay)) {
return true;
}
}
return false;
*注意:这是未经测试的,索引可能会偏离+ -1,并且可能存在一些边缘情况。使用风险自负。
答案 4 :(得分:0)
如果它真的是关于速度的,并且你想要搜索项目列表而不是实体字符串,你可以将工作分成不同的线程(我不确定你要检查多少项目,但是如果它没有花费几分钟,这可能不是要走的路)
如果你不需要将大海捞针变成一个数组,你可以反而遍历针,并通过String.contains()测试haystack;