Вот реализация Java, которая находит максимальное перекрытие между двумя строками с длиной N и M в чем-то вроде операций O (min (N, M)) ~ O (N).
У меня была та же идея, что и @ sepp2k: теперь удалили ответ и немного поработали над этим. Кажется, хорошо работает. Идея состоит в том, чтобы выполнить итерацию первой строки и начать отслеживание, как только вы найдете что-то, что соответствует началу второй строки. Выяснилось, что вам может потребоваться несколько одновременных отслеживаний, если совпадения ложных и истинных совпадений. В конце вы выбираете самый длинный трек.
Я havent разработал абсолютно худший случай, с максимальным совпадением между матчами, но я не ожидаю, что он вырвется из-под контроля, так как я думаю, что вы не можете совместить произвольное множество матчей. Обычно вы отслеживаете только одно или два матча за раз: кандидаты удаляются, как только происходит несоответствие.
static class Candidate {
int matchLen = 0;
}
private String overlapOnce(@NotNull final String a, @NotNull final String b) {
final int maxOverlap = Math.min(a.length(), b.length());
final Collection<Candidate> candidates = new LinkedList<>();
for (int i = a.length() - maxOverlap; i < a.length(); ++i) {
if (a.charAt(i) == b.charAt(0)) {
candidates.add(new Candidate());
}
for (final Iterator<Candidate> it = candidates.iterator(); it.hasNext();) {
final Candidate candidate = it.next();
if (a.charAt(i) == b.charAt(candidate.matchLen)) {
//advance
++candidate.matchLen;
} else {
//not matching anymore, remove
it.remove();
}
}
}
final int matchLen = candidates.isEmpty() ? 0 :
candidates.stream().map(c -> c.matchLen).max(Comparator.comparingInt(l -> l)).get();
return a + b.substring(matchLen);
}
private String overlapOnce(@NotNull final String... strings) {
return Arrays.stream(strings).reduce("", this::overlapOnce);
}
И некоторые тесты:
@Test
public void testOverlapOnce() throws Exception {
assertEquals("", overlapOnce("", ""));
assertEquals("ab", overlapOnce("a", "b"));
assertEquals("abc", overlapOnce("ab", "bc"));
assertEquals("abcdefghqabcdefghi", overlapOnce("abcdefgh", "efghqabcdefghi"));
assertEquals("aaaaaabaaaaaa", overlapOnce("aaaaaab", "baaaaaa"));
assertEquals("ccc", overlapOnce("ccc", "ccc"));
assertEquals("abcabc", overlapOnce("abcabc", "abcabc"));
/**
* "a" + "b" + "c" => "abc"
"abcde" + "defgh" + "ghlmn" => "abcdefghlmn"
"abcdede" + "dedefgh" + "" => "abcdedefgh"
"abcde" + "d" + "ghlmn" => "abcdedghlmn"
"abcdef" + "" + "defghl" => "abcdefghl"
*/
assertEquals("abc", overlapOnce("a", "b", "c"));
assertEquals("abcdefghlmn", overlapOnce("abcde", "defgh", "ghlmn"));
assertEquals("abcdedefgh", overlapOnce("abcdede", "dedefgh"));
assertEquals("abcdedghlmn", overlapOnce("abcde", "d", "ghlmn"));
assertEquals("abcdefghl", overlapOnce("abcdef", "", "defghl"));
// Consider str1=abXabXabXac and str2=XabXac. Your approach will output abXabXabXacXabXac because by
// resetting j=0, it goes to far back.
assertEquals("abXabXabXac", overlapOnce("abXabXabXac", "XabXac"));
// Try to trick algo with an earlier false match overlapping with the real match
// - match first "aba" and miss that the last "a" is the start of the
// real match
assertEquals("ababa--", overlapOnce("ababa", "aba--"));
}
Что средняя длина строки? – RBarryYoung
Только средняя строка длинна, в среднем около 100 символов. Префикс и постфикс составляют всего около 20 символов. Это не имеет большого значения. Преобразование базы данных происходит только один раз. Это займет несколько дней, но всего лишь несколько часов будут потрачены на объединение строк в худшем случае. Я пытаюсь найти оптимальное решение, просто для удовольствия и сложности. Кажется, я нашел его. Благодаря! –