Java indexOf(蛮力方法)对我或其他子字符串算法更实用吗?
我正在寻找在许多短文本行(haystack)中找到非常短的子字符串(模式、针)。然而,我不太确定除了天真、暴力的方法之外,应该使用哪种方法 背景:我正在做一个有趣的附带项目,在那里我收到多个用户的短信聊天日志(从2000-15000行文字到2-50个用户),我想根据我想到的预定单词在聊天日志中找到所有不同的模式匹配。到目前为止,我有大约1600个模式,我正在寻找,但我可能会寻找更多 例如,我想找出一般短信日志中使用的食品相关词语的数量,如“汉堡”、“比萨饼”、“可乐”、“午餐”、“晚餐”、“餐厅”、“麦当劳”。虽然我给出了英语示例,但实际上我的程序将使用韩语。每一个指定的单词都有各自的分数,我将其作为键和值分别放在hashmap中。然后,我展示了与食物相关的单词的得分最高的人,以及那些用户最常用的食物单词 我目前的方法是用空格消除每一行文本,并使用haystack包含模式的contains方法(使用indexOf方法和朴素的子字符串搜索算法)处理haystack中的每个单词Java indexOf(蛮力方法)对我或其他子字符串算法更实用吗?,java,string,algorithm,substring,Java,String,Algorithm,Substring,我正在寻找在许多短文本行(haystack)中找到非常短的子字符串(模式、针)。然而,我不太确定除了天真、暴力的方法之外,应该使用哪种方法 背景:我正在做一个有趣的附带项目,在那里我收到多个用户的短信聊天日志(从2000-15000行文字到2-50个用户),我想根据我想到的预定单词在聊天日志中找到所有不同的模式匹配。到目前为止,我有大约1600个模式,我正在寻找,但我可能会寻找更多 例如,我想找出一般短信日志中使用的食品相关词语的数量,如“汉堡”、“比萨饼”、“可乐”、“午餐”、“晚餐”、“餐厅
wordFromInput.contains(wordFromPattern);
举个例子,聊天室有17个用户,13000行文本,1600个模式,我发现使用这种方法整个程序需要12-13秒。而在我正在开发的Android应用程序上,它需要2分30秒来处理,这太慢了
最初,我尝试使用哈希映射,只获取模式,而不是在ArrayList中搜索它,但后来我意识到这是
对于我试图用一个子串做的事情
我在Stackoverflow中查找了很多有用的相关问题,例如以下两个:
和。我比较熟悉各种字符串算法(Boyer Moore、KMP等)
我最初认为,对于我的案例来说,naive方法当然是最糟糕的算法类型,但经过发现,我意识到我的案例(短模式,短文本)可能会更有效地使用naive方法。但我想知道是否有什么东西我完全忽略了
如果有人想更具体地看待我的问题,这里有一个建议
虽然我删除了大部分代码以简化它,但我用于实际匹配子字符串的主要方法存在于matchWords()方法中
我知道这是非常丑陋和糟糕的代码(5个循环…),所以如果有任何建议,我也很高兴听到它
因此,要清理它:
- 聊天日志中的文字行(2000-10000+),haystack
- 1600多个图案,针
- 大部分使用韩文字符,但也包括一些英文字符
- 蛮力天真的方法实在太慢了,但考虑到短模式和文本的性质,是否还有其他替代方法,即使有,它们是否实用,仍存在争议
我只想在我的思考过程中得到一些意见,可能还有一些一般性的建议。但是,另外,如果可能的话,我想对特定的算法或方法提出一些具体的建议。我非常确定
string.contains
已经进行了高度优化,因此用其他方法替换它不会对您有多大好处
因此,我想,要做的不是在聊天词中查找每个银行词,而是一次进行多重比较
第一种方法是创建一个巨大的正则表达式来匹配所有银行单词。编译它,希望正则表达式包足够有效(很可能是这样)。您将有一个相当长的设置阶段(regex编译),但匹配应该快得多。您可以为需要匹配的单词建立索引,并在处理它们时对它们进行计数。如果您可以使用HashMap来查找每个单词的模式,那么成本将是
O(n*m)
您可以对所有可能的单词使用HashMap,然后可以稍后对单词进行剖析
e、 假设你需要匹配红色和苹果,你可以将
redapple = 1
applered = 0
red = 10
apple = 15
这意味着红色实际上是11(10+1),苹果是16(15+1)我不懂韩语,所以我想用韩语修补字符串的相同策略不一定像英语那样可行,但也许这个伪代码策略可以用你的韩语知识来实现。(Java当然还是一样的,但例如,在韩语中,字母“ough”很可能是连续的吗?甚至还有字母表示“ough”?但是,尽管如此,希望这一原则能够得到应用 我将使用String.ToCharray创建二维数组(如果需要可变大小,则使用ArrayList) 我建议跳到最后一个字母的原因是,从统计上看,一个单词的前两个字母的“辅音,元音”非常高,尤其是名词,因为任何食物都是名词(几乎你给出的所有关键字示例都与辅音,元音的结构相匹配)由于只有5个元音(加上y),第二个字母“i”出现在关键字“pizza”中的可能性很高,但在这一点之后,这个单词很有可能不匹配 然而,如果你知道第一个字母和最后一个字母匹配,那么你可能有一个更强的候选字母,然后可以反向迭代。我认为在更大的数据集上,这将比按顺序检查字母更快地消除候选字母。基本上,你会
if (first letter of word matches keyword's first letter)//we have a candidate
skip to last letter of the current word //see comment below
if(last letter of word matches keyword's last letter)//strong candidate
iterate backwards to start+1 checking remainder of letters
private static class Matcher {
private final int where;
private final String s;
private int i = 0;
public Matcher ( String s, int where ) {
this.s = s;
this.where = where;
}
public boolean match(char ch) {
return s.charAt(i++) == ch;
}
public int matched() {
return i == s.length() ? where: -1;
}
}
// Words I am looking for.
String[] watchFor = new String[] {"flies", "like", "arrow", "banana", "a"};
// Test string to search.
String test = "Time flies like an arrow, fruit flies like a banana";
public void test() {
// Use a LinkedList because it is O(1) to remove anywhere.
List<Matcher> matchers = new LinkedList<> ();
int pos = 0;
for ( char c : test.toCharArray()) {
// Fire off all of the matchers at this point.
for ( String s : watchFor ) {
matchers.add(new Matcher(s, pos));
}
// Discard all matchers that fail here.
for ( Iterator<Matcher> i = matchers.iterator(); i.hasNext(); ) {
Matcher m = i.next();
// Should it be removed?
boolean remove = !m.match(c);
if ( !remove ) {
// Still matches! Is it complete?
int matched = m.matched();
if ( matched >= 0 ) {
// Todo - Should use getters.
System.out.println(" "+m.s +" found at "+m.where+" active matchers "+matchers.size());
// Complete!
remove = true;
}
}
// Remove it where necessary.
if ( remove ) {
i.remove();
}
}
// Step pos to keep track.
pos += 1;
}
}
flies found at 5 active matchers 6
like found at 11 active matchers 6
a found at 16 active matchers 2
a found at 19 active matchers 2
arrow found at 19 active matchers 6
flies found at 32 active matchers 6
like found at 38 active matchers 6
a found at 43 active matchers 2
a found at 46 active matchers 3
a found at 48 active matchers 3
banana found at 45 active matchers 6
a found at 50 active matchers 2
private Map<String, Set<Integer>> index; // pre-populated
Set<Integer> search(String... topics) {
Set<Integer> results = null;
for (String topic : topics) {
Set<Integer> hits = index.get(topic);
if (hits == null)
return Collections.emptySet();
if (results == null)
results = new HashSet<Integer>(hits);
else
results.retainAll(hits);
if (results.isEmpty())
return Collections.emptySet(); // exit early
}
return results;
}