检查大海捞针是否包含一组针的最快方法

时间:2016-10-08 17:15:18

标签: java string algorithm pattern-matching

我有一个 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!

5 个答案:

答案 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;