Найти длину самой длинной общей подстроки в двоичных строках ‘n’

Мне дают n строки (n> = 2 и n<= 4) и каждый составлен только из 2 букв: a а также b, В этом наборе строк мне нужно найти длину самой длинной общей подстроки, присутствующей во всех строках. Решение гарантированно существует. Давайте посмотрим на пример:

n=4
abbabaaaaabb
aaaababab
bbbbaaaab
aaaaaaabaaab

The result is 5 (because the longest common substring is "aaaab").

Мне не нужно печатать (или даже знать) подстроку, мне просто нужно напечатать ее длину.

Также дано, что результат не может быть больше, чем 60даже если длина каждой строки может достигать 13 000,

Я попробовал следующее: я нашел наименьшую длину любой строки из указанных строк, а затем сравнил ее с 60 и я выбрал наименьшее значение между двумя, как starting point, Затем я начал брать последовательности первой строки, и длина каждой последовательности первой строки len, где len принимает значения от starting point в 1, На каждой итерации я беру все возможные последовательности первой строки длины len и я использую это как pattern, Используя алгоритм KMP (таким образом, сложность O(n+m)), Я перебрал все остальные строки (из 2 в n) и проверил, если pattern находится в строке i, Всякий раз, когда это не найдено, я ломаю итерацию и пробую следующую доступную последовательность длины len или, если нет, я уменьшаю len и попробуйте все последовательности, которые имеют в качестве длины новое, уменьшенное значение len, Но если это совпадает, я останавливаю программу и печатаю длину lenПоскольку мы начинали с самой длинной возможной длины, уменьшающейся на каждом шаге, логично, что первое найденное совпадение представляет наибольшую возможную длину. Вот код (но это на самом деле не имеет значения, так как этот метод недостаточно хорош; я знаю, что не должен использовать using namespace std но это на самом деле не влияет на эту программу, поэтому я просто не стал беспокоиться):

#include <iostream>
#include <string>
#define nmax 50001
#define result_max 60

using namespace std;

int n,m,lps[nmax],starting_point,len;
string a[nmax],pattern,str;

void create_lps() {
lps[0]=0;
unsigned int len=0,i=1;
while (i < pattern.length()) {
if (pattern[i] == pattern[len]) {
len++;
lps[i] = len;
i++;
}
else {
if (len != 0) {
len = lps[len-1];
}
else {
lps[i] = 0;
i++;
}
}
}
}

bool kmp_MatchOrNot(int index) {
unsigned int i=0,j=0;
while (i < a[index].length()) {
if (pattern[j] == a[index][i]) {
j++;
i++;
}
if (j == pattern.length()) {
return true;
}
else if (i<a[index].length() && pattern[j]!=a[index][i]){
if (j != 0) {
j = lps[j-1];
}
else {
i++;
}
}
}
return false;
}

int main()
{
int i,left,n;
unsigned int minim = nmax;
bool solution;
cin>>n;
for (i=1;i<=n;i++) {
cin>>a[i];
if (a[i].length() < minim) {
minim = a[i].length();
}
}

if (minim < result_max) starting_point = minim;
else starting_point = result_max;

for (len=starting_point; len>=1; len--) {
for (left=0; (unsigned)left<=a[1].length()-len; left++) {
pattern = a[1].substr(left,len);
solution = true;
for (i=2;i<=n;i++) {
if (pattern.length() > a[i].length()) {
solution = false;
break;
}
else {
create_lps();
if (kmp_MatchOrNot(i) == false) {
solution = false;
break;
}
}
}
if (solution == true) {
cout<<len;
return 0;
}
}
}
return 0;
}

Дело в том, что программа работает правильно и дает правильные результаты, но когда я отправил код на веб-сайт, он выдал ошибку «Превышен лимит времени», поэтому я получил только половину баллов.

Это заставляет меня поверить, что для решения проблемы в более сжатые сроки мне нужно воспользоваться тем фактом, что буквы строки могут быть только a или же b, поскольку это выглядит довольно большой вещью, которую я не использовал, но я понятия не имею, как именно я могу использовать эту информацию. Буду признателен за любую помощь.

0

Решение

Ответ заключается в том, чтобы построить суффиксные деревья всех строк по отдельности, а затем пересечь их. Дерево суффиксов похоже на дерево, содержащее все суффиксы одной строки одновременно.

Построение дерева суффиксов для фиксированного алфавита O(n) с Алгоритм Укконена. (Если вам не нравится это объяснение, вы можете использовать Google, чтобы найти других.) Если у вас есть m деревья размером nэто время O(nm),

Пересечение суффиксных деревьев — это вопрос их параллельного прохождения, только если вы продвигаетесь дальше по всем деревьям. Если у вас есть m деревья размером nэта операция может быть выполнена в срок не более O(nm),

Общее время этого алгоритма — время O(nm), Учитывая, что просто чтение строк имеет время O(nm)Вы не можете сделать лучше, чем это.


Добавляя небольшое количество деталей, предположим, что ваше дерево суффиксов записано в виде одного символа на узел. Таким образом, каждый узел — это просто словарь, ключи которого являются символами, а значения — остальной частью дерева. Так что нам ваш пример, для строки ABABA диаграмма в https://imgur.com/a/tnVlSI1 превратится в структуру данных что-то вроде (см. ниже) этого:

{
'A': {
'B': {
'': None,
'A': {
'B': {
'': None
}
}
}
},
'B': {
'': None
'A': {
'B': {
'': None
}
}
}
}

И аналогично BABA превратится в:

{
'A': {
'': None
'B': {
'A': {
'': None
}
}
},
'B': {
'A': {
'': None,
'B': {
'A': {
'': None
}
}
}
}
}

Со структурами данных, которые выглядят так, наивный Python для их сравнения выглядит так:

def tree_intersection_depth (trees):
best_depth = 0
for (char, deeper) in trees[0].items():
if deeper is None:
continue
failed = False

deepers = [deeper]
for tree in trees[1:]:
if char in tree:
deepers.append(tree[char])
else:
failed = True
break

if failed:
continue

depth = 1 + tree_intersection_depth(deepers)
if best_depth < depth:
best_depth = depth

return best_depth

И вы бы назвали это как tree_intersection_depth([tree1, tree2, tree3, ...]),

С двумя вышеупомянутыми деревьями это действительно дает 3 как ответ.

Теперь я фактически обманул, написав эту структуру данных. Что делает суффикс-деревья эффективными, так это то, что у вас на самом деле нет структуры данных, которая выглядит так. У вас есть тот, который повторно использует все повторяющиеся структуры. Таким образом, код для имитации настройки структур данных и их вызова выглядит следующим образом:

b_ = {'B': {'': None}}
ab_ = {'': None, 'A': b_}
bab_ = {'B': ab_}
abab = {'A': bab_, 'B': ab_}

a_ = {'A': {'': None}}
ba_ = {'': None, 'B': a_}
aba_ = {'A': ba_}
baba = {'B': aba_, 'A': ba_}

print(tree_intersection_depth([abab, baba]))

И теперь мы видим, что для получения обещанной производительности не хватает шага. Проблема в том, что в то время как размер дерева O(n)во время поиска мы обязательно посетим O(n^2) подстроки. В вашем случае вам не нужно беспокоиться об этом, потому что подстроки гарантированно никогда не пойдут на глубину более 60. Но в общем случае вам нужно будет добавить памятку, чтобы при рекурсии сравнивались структуры данных, которые вы видел раньше, вы сразу возвращаете старый ответ, а не новый. (В Python вы бы использовали id() способ сравнить адрес объекта с теми, которые вы видели раньше. В C ++ есть набор кортежей указателей для той же цели.)

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]