我有一组大小约为100-200的元素。假设样本元素为X
。
每个元素都是一组字符串(这样的一组中的字符串数在1到4之间)。 X
= {s1
,s2
,s3
}
对于给定的输入字符串(大约100个字符),比如P
,我想测试字符串中是否有X
存在。
X
目前 P
iff中所有s
属于X
,s
是P
的子字符串1}}。
元素集可用于预处理。
我希望在Java中尽可能快。可能的方法不符合我的要求:
s
是否为P
的子字符串似乎是一项代价高昂的操作s
可以是P
的任何子字符串(不一定是单词),所以我不能使用单词的哈希值s1
,s2
,s3
可以按任何顺序出现,所有字符串都需要作为子字符串存在现在我的方法是用每个X
构建一个巨大的正则表达式,其中包含字符串顺序的所有可能排列。因为X
<= 4中的元素数量,这仍然是可行的。如果有人可以指出我更好(更快/更优雅)的方法,那将是很好的。
请注意,这组元素可用于预处理,我希望在java中使用该解决方案。
答案 0 :(得分:2)
你可以直接使用正则表达式:
Pattern regex = Pattern.compile(
"^ # Anchor search to start of string\n" +
"(?=.*s1) # Check if string contains s1\n" +
"(?=.*s2) # Check if string contains s2\n" +
"(?=.*s3) # Check if string contains s3",
Pattern.DOTALL | Pattern.COMMENTS);
Matcher regexMatcher = regex.matcher(subjectString);
foundMatch = regexMatcher.find();
如果字符串中存在所有三个子字符串,则 foundMatch
为真。
请注意,如果它们可能包含正则表达式元字符,则可能需要转义“”字符串“。
答案 1 :(得分:1)
听起来你在实际发现特定方法实际上太慢之前过早地优化了代码。
关于你的字符串集的nice属性是字符串必须包含X
的所有元素作为子字符串 - 这意味着如果我们找到一个未包含的X
元素,我们就会快速失败在P
内。这可能会比其他方法更好地节省时间,特别是如果X
的元素通常长于几个字符并且不包含或仅包含少量重复字符。例如,当检查是否存在具有非重复字符(例如,惯性)的5长度字符串时,正则表达式引擎仅需要检查100个长度字符串中的20个字符。而且,由于X
有100-200个元素,你真的想要尽快失败。
我的建议是按照长度顺序对字符串进行排序,并依次检查每个字符串,如果找不到一个字符串则提前停止。
答案 2 :(得分:1)
看起来像Rabin–Karp algorithm的完美案例:
Rabin-Karp因单一模式搜索Knuth-Morris-Pratt算法,Boyer-Moore字符串搜索算法以及其他更快速的单模式字符串搜索算法而劣势,因为它具有缓慢的最坏情况行为。然而,Rabin-Karp是多模式搜索的首选算法。
答案 3 :(得分:0)
当预处理时间无关紧要时,您可以创建一个哈希表,将每个字母,两个字母,三个字母等组合映射到至少一个字符串中的字符串列表中。发生。
索引字符串的算法看起来像那样(未经测试):
HashMap<String, Set<String>> indexes = new HashMap<String, Set<String>>();
for (int pos = 0; pos < string.length(); pos++) {
for (int sublen=0; sublen < string.length-pos; sublen++) {
String substring = string.substr(pos, sublen);
Set<String> stringsForThisKey = indexes.get(substring);
if (stringsForThisKey == null) {
stringsForThisKey = new HashSet<String>();
indexes.put(substring, stringsForThisKey);
}
stringsForThisKey.add(string);
}
}
将每个字符串索引为字符串长度的二次方,但每个字符串只需要执行一次。
但结果是对发生特定字符串的字符串列表进行恒速访问。
答案 4 :(得分:0)
您可能正在寻找Aho-Corasick algorithm,它从字符串集(字典)构建自动机(类似于trie),并尝试使用此自动机将输入字符串与字典进行匹配。
答案 5 :(得分:0)
一种方法是生成每个可能的子字符串并将其添加到集合中。这非常低效。
相反,您可以从任意点到最后创建所有字符串到NavigableSet并搜索最接近的匹配。如果最接近的匹配以您要查找的字符串开头,则您具有子字符串匹配。
static class SubstringMatcher {
final NavigableSet<String> set = new TreeSet<String>();
SubstringMatcher(Set<String> strings) {
for (String string : strings) {
for (int i = 0; i < string.length(); i++)
set.add(string.substring(i));
}
// remove duplicates.
String last = "";
for (String string : set.toArray(new String[set.size()])) {
if (string.startsWith(last))
set.remove(last);
last = string;
}
}
public boolean findIn(String s) {
String s1 = set.ceiling(s);
return s1 != null && s1.startsWith(s);
}
}
public static void main(String... args) {
Set<String> strings = new HashSet<String>();
strings.add("hello");
strings.add("there");
strings.add("old");
strings.add("world");
SubstringMatcher sm = new SubstringMatcher(strings);
System.out.println(sm.set);
for (String s : "ell,he,ow,lol".split(","))
System.out.println(s + ": " + sm.findIn(s));
}
打印
[d, ello, ere, hello, here, ld, llo, lo, old, orld, re, rld, there, world]
ell: true
he: true
ow: false
lol: false
答案 6 :(得分:0)
您可能还想考虑使用“后缀树”。我没有使用过此代码,但有一个描述here
我使用了专有实现(我甚至无法访问)并且它们非常快。