我正在尝试创建一个程序,以尽可能快地检测字符串中是否有多个单词,如果是,则执行一个行为。最好,我也希望它能够检测这些单词的顺序,但前提是这可以快速完成。到目前为止,这就是我所做的:
if (input.contains("adsf") && input.contains("qwer")) {
execute();
}
如您所见,对多个单词执行此操作会变得很烦人。这是检测多个子字符串的唯一方法还是有更好的方法?有什么方法可以检测顺序吗?
我会从以下单词创建一个正则表达式:
Pattern pattern = Pattern.compile("(?=.*adsf)(?=.*qwer)");
if (pattern.matcher(input).find()) {
execute();
}
有关更多详细信息,请参阅此答案:https ://stackoverflow.com/a/470602/660143
编者注:尽管被大力支持和接受,但它的功能与问题中的代码不同。
execute
在第一次匹配时调用,如逻辑 OR。
你可以使用一个数组:
String[] matches = new String[] {"adsf", "qwer"};
bool found = false;
for (String s : matches)
{
if (input.contains(s))
{
execute();
break;
}
}
这与您发布的一样有效,但更易于维护。寻找一个更有效的解决方案听起来像是一个微优化,在被证明是你的代码的有效瓶颈之前应该忽略它,在任何情况下,如果有一个巨大的字符串集,这个解决方案可能是一个尝试。
在 Java 8 中你可以做
public static boolean containsWords(String input, String[] words) {
return Arrays.stream(words).allMatch(input::contains);
}
示例用法:
String input = "hello, world!";
String[] words = {"hello", "world"};
if (containsWords(input, words)) System.out.println("Match");
如果您有很多子字符串要查找,那么正则表达式可能不会有太大帮助,因此您最好将子字符串放在一个列表中,然后遍历它们并调用input.indexOf(substring)
每个子字符串。这将返回int
找到子字符串的位置的索引。如果您将每个结果(-1 除外,这意味着未找到子字符串)放入 a TreeMap
(其中是键,子字符串是值),那么您可以通过调用mapindex
来按顺序检索它们。keys()
Map<Integer, String> substringIndices = new TreeMap<Integer, String>();
List<String> substrings = new ArrayList<String>();
substrings.add("asdf");
// etc.
for (String substring : substrings) {
int index = input.indexOf(substring);
if (index != -1) {
substringIndices.put(index, substring);
}
}
for (Integer index : substringIndices.keys()) {
System.out.println(substringIndices.get(index));
}
使用树结构来保存每个代码点的子字符串。这消除了需要
请注意,这只有在针组几乎恒定时才有效。虽然单独添加或删除子字符串并不是低效的,但是每次将大量字符串排列成树结构的不同初始化肯定会减慢它。
StringSearcher
:import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
class StringSearcher{
private NeedleTree needles = new NeedleTree(-1);
private boolean caseSensitive;
private List<Integer> lengths = new ArrayList<>();
private int maxLength;
public StringSearcher(List<String> inputs, boolean caseSensitive){
this.caseSensitive = caseSensitive;
for(String input : inputs){
if(!lengths.contains(input.length())){
lengths.add(input.length());
}
NeedleTree tree = needles;
for(int i = 0; i < input.length(); i++){
tree = tree.child(caseSensitive ? input.codePointat(i) : Character.toLowerCase(input.codePointAt(i)));
}
tree.markSelfSet();
}
maxLength = Collections.max(legnths);
}
public boolean matches(String haystack){
if(!caseSensitive){
haystack = haystack.toLowerCase();
}
for(int i = 0; i < haystack.length(); i++){
String substring = haystack.substring(i, i + maxLength); // maybe we can even skip this and use from haystack directly?
NeedleTree tree = needles;
for(int j = 0; j < substring.maxLength; j++){
tree = tree.childOrNull(substring.codePointAt(j));
if(tree == null){
break;
}
if(tree.isSelfSet()){
return true;
}
}
}
return false;
}
}
NeedleTree.java
:import java.util.HashMap;
import java.util.Map;
class NeedleTree{
private int codePoint;
private boolean selfSet;
private Map<Integer, NeedleTree> children = new HashMap<>();
public NeedleTree(int codePoint){
this.codePoint = codePoint;
}
public NeedleTree childOrNull(int codePoint){
return children.get(codePoint);
}
public NeedleTree child(int codePoint){
NeedleTree child = children.get(codePoint);
if(child == null){
child = children.put(codePoint, new NeedleTree(codePoint));
}
return child;
}
public boolean isSelfSet(){
return selfSet;
}
public void markSelfSet(){
selfSet = true;
}
}
这是一个经典的面试和 CS 问题。
Robin Karp 算法通常是人们在采访中首先谈论的内容。基本思想是,在遍历字符串时,将当前字符添加到散列中。如果哈希与您的一个匹配字符串的哈希匹配,则您知道您可能有一个匹配项。这避免了在匹配字符串中来回扫描。 https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm
该面试问题的其他典型主题是考虑使用 trie 结构来加快查找速度。如果您有大量匹配字符串,则必须始终检查大量匹配字符串。trie 结构更有效地进行检查。 https://en.wikipedia.org/wiki/Trie
其他算法是: - Aho–Corasick https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm - Commentz-Walter https://en.wikipedia.org/wiki/Commentz-Walter_algorithm
我认为更好的方法是这样的,我们可以将多个值添加为一个字符串,并通过函数的索引验证索引
String s = "123";
System.out.println(s.indexOf("1")); // 0
System.out.println(s.indexOf("2")); // 1
System.out.println(s.indexOf("5")); // -1