Найти длину самой длинной общей подстроки в двоичных строках 'n' - PullRequest
0 голосов
/ 03 октября 2018

Мне дано n строк (n> = 2 и n <= 4), каждая из которых состоит только из 2 букв: <code>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,так как это выглядит довольно большой вещью, которую я не использовал, но я понятия не имею, как именно я могу использовать эту информацию.Буду признателен за любую помощь.

1 Ответ

0 голосов
/ 03 октября 2018

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

Построение дерева суффиксов для фиксированного алфавита составляет 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 ++ для этой же цели есть набор кортежей указателей.)

...