Прежде всего, неясно, как или почему алгоритм не соответствует вашим потребностям в отношении корректности, когда Trie
построен с ignoreOverlaps()
.Хотя, я верю вашему слову об этом.Я также готов верить вам, когда вы говорите, что в этом случае это влияет на производительность.
Так что вместо того, чтобы углубляться в реализацию алгоритма, я бы лучше использовал его с перекрытиями, а затемудалите перекрытия вручную.В этом случае, я думаю, вы сможете точно настроить, какой из них пропустить.
Вот код для инициализации Trie
:
String text = ... // read the text somewhere
Set<String> keywords = new HashSet<>();
keywords.add("keyword1");
keywords.add("keyword2");
Trie trie = Trie.builder().addKeywords(keywords).build(); // with overlaps!
Теперь давайте разберем текст:
Collection<Emit> parseResults = trie.parseText(text);
Насколько я могу судить, результаты разбора возвращаются в порядке появления в тексте, но я не проверял это тщательно.Чтобы приведенный ниже код работал правильно, нам нужно, чтобы выбросы были отсортированы по стартовому индексу.
Учитывая, что выбросы отсортированы по стартовому индексу, вот код для подсчета непересекающихся выбросов по ключевому слову:
Map<String, Long> map = parseResults.stream()
.collect(Collectors.groupingBy(Emit::getKeyword, countingNonOverlaps()));
Где служебный метод countingNonOverlaps()
выглядит следующим образом:
private static Collector<Emit, ?, Long> countingNonOverlaps() {
class Acc {
Emit last;
long count = 0;
void add(Emit e) {
if (last == null || !last.overlapsWith(e)) { count++; last = e; }
}
Acc merge(Acc another) {
throw new UnsupportedOperationException("Parallel not supported");
}
}
return Collector.of(Acc::new, Acc::add, Acc::merge, acc -> acc.count);
}
В этом подходе используется собственный коллектор для подсчета неперекрывающихся выбросов за ключевое слово.Существуют и другие, более простые способы сделать это без специального сборщика, но они требуют вести список неперекрывающихся выбросов для каждого ключевого слова.Поскольку вам нужны только цифры и поскольку вы работаете с 2000 ключевыми словами и огромным текстом, я думаю, что этот способ лучше.
Сборщик в основном отслеживает последний не перекрывающийся собранный выброс и увеличивает счетчиктекущий эмитт собирается только в том случае, если он не перекрывается с последним не перекрывающимся излучением.Кроме того, он работает только для последовательных потоков.
Примечание. Если вам необходимо выполнить точную настройку при увеличении счетчика, вы можете настроить метод add
локального класса Acc
.