Поиск всех общих подстрок заданных двух строк

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

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

Например, с учетом входных строк eatsleepnightxyz и eatsleepabcxyz, результаты должны быть:

  • eatsleep (из-за eatsleepnightxyz eatsleepabcxyz)
  • xyz (из-за eatsleepnightxyz eatsleepabcxyz)
  • a (из-за eatsleepnightxyz eatsleepabcxyz)
  • t (из-за eatsleepnightxyz eatsleepabcxyz)

Однако набор результатов не должен включать e из eatsleepnightxyz eatsleepabcxyz, поскольку оба e уже содержатся в упомянутом выше eatsleep. Вы также не должны включать ea, eat, ats и т.д., Так как все они покрываются eatsleep.

В этом случае вам не нужно использовать методы командной строки String, такие как: contains, indexOf, StringTokenizer, split и replace.

Мой алгоритм выглядит следующим образом: я начинаю с грубой силы и переключусь на более оптимизированное решение, когда улучшу свое базовое понимание.

 For String S1:
     Find all the substrings of S1 of all the lengths
     While doing so: Check if it is also a substring of 
     S2.

Попытка выяснить временную сложность моего подхода.

Пусть две заданные строки: n1-String и n2-String

  • Число подстрок S1, очевидно, равно n1 (n1 + 1)/2.
  • Но нам нужно найти среднюю длину подстроки S1.
  • Допустим, что это m. Хорошо найти m отдельно.
  • Сложность времени для проверки того, является ли m-String подстрокой n-String - O (n * m).
  • Теперь мы проверяем, что каждая m-String является подстрокой S2, который является n2-String.
  • Это, как мы видели выше, является алгоритмом O (n 2 m).
  • Время, требуемое общим алгоритмом, тогда
  • Tn = (количество подстрок в S1) * (средняя длина подстроки для процедуры сравнения символов)
  • Выполняя определенные вычисления, я пришел к выводу, что временная сложность O (n 3 m 2)
  • Теперь наша задача - найти m в терминах n1.

Попытка найти m в терминах n1.

T n= (n) (1) + (n-1) (2) + (n-2) (3) +..... + (2) (n- 1) + (1) (n)
где T n - сумма длин всех подстрок.

Среднее будет деление этой суммы на общее количество созданных подстрок.

Это просто задача суммирования и деления, решение которой выглядит следующим образом: O (n)

Поэтому...

Время работы моего алгоритма O (n ^ 5).

С учетом этого я написал следующий код:

 package pack.common.substrings;

 import java.util.ArrayList;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;

 public class FindCommon2 {
    public static final Set<String> commonSubstrings = new      LinkedHashSet<String>();

 public static void main(String[] args) {
    printCommonSubstrings("neerajisgreat", "neerajisnotgreat");
    System.out.println(commonSubstrings);
}

 public static void printCommonSubstrings(String s1, String s2) {
    for (int i = 0; i < s1.length();) {
        List<String> list = new ArrayList<String>();
        for (int j = i; j < s1.length(); j++) {
            String subStr = s1.substring(i, j + 1);
            if (isSubstring(subStr, s2)) {
                list.add(subStr);
            }
        }
        if (!list.isEmpty()) {
            String s = list.get(list.size() - 1);
            commonSubstrings.add(s);
            i += s.length();
        }
    }
 }

 public static boolean isSubstring(String s1, String s2) {
    boolean isSubstring = true;
    int strLen = s2.length();
    int strToCheckLen = s1.length();
    if (strToCheckLen > strLen) {
        isSubstring = false;
    } else {
        for (int i = 0; i <= (strLen - strToCheckLen); i++) {
            int index = i;
            int startingIndex = i;
            for (int j = 0; j < strToCheckLen; j++) {
                if (!(s1.charAt(j) == s2.charAt(index))) {
                    break;
                } else {
                    index++;
                }
            }
            if ((index - startingIndex) < strToCheckLen) {
                isSubstring = false;
            } else {
                isSubstring = true;
                break;
            }
        }
    }
    return isSubstring;
 }
}

Объяснение для моего кода:

 printCommonSubstrings: Finds all the substrings of S1 and 
                        checks if it is also a substring of 
                        S2.
 isSubstring : As the name suggests, it checks if the given string 
               is a substring of the other string.

Проблема: с учетом входных данных

  S1 = "neerajisgreat";
  S2 = "neerajisnotgreat"
  S3 = "rajeatneerajisnotgreat"

В случае S1 и S2 выход должен быть: neerajis и great но в случае S1 и S3 выход должен был быть: neerajis, raj, great, eat, но все же я получаю neerajis и great как вывод. Мне нужно понять это.

Как мне создать код?

Ответы

Ответ 1

Вам было бы лучше иметь правильный алгоритм для задачи, а не подход грубой силы. Википедия описывает два общих решения самая длинная общая проблема подстроки: и .

Решение динамического программирования принимает время O (n m) и O (n m). Это довольно простой перевод на Java псевдокода Wikipedia для самой длинной общей подстроки:

public static Set<String> longestCommonSubstrings(String s, String t) {
    int[][] table = new int[s.length()][t.length()];
    int longest = 0;
    Set<String> result = new HashSet<>();

    for (int i = 0; i < s.length(); i++) {
        for (int j = 0; j < t.length(); j++) {
            if (s.charAt(i) != t.charAt(j)) {
                continue;
            }

            table[i][j] = (i == 0 || j == 0) ? 1
                                             : 1 + table[i - 1][j - 1];
            if (table[i][j] > longest) {
                longest = table[i][j];
                result.clear();
            }
            if (table[i][j] == longest) {
                result.add(s.substring(i - longest + 1, i + 1));
            }
        }
    }
    return result;
}

Теперь вам нужны все обычные подстроки, а не самые длинные. Вы можете улучшить этот алгоритм, включив в него более короткие результаты. Рассмотрим таблицу для входов eatsleepnightxyz и eatsleepabcxyz:

  e a t s l e e p a b c x y z
e 1 0 0 0 0 1 1 0 0 0 0 0 0 0
a 0 2 0 0 0 0 0 0 1 0 0 0 0 0
t 0 0 3 0 0 0 0 0 0 0 0 0 0 0
s 0 0 0 4 0 0 0 0 0 0 0 0 0 0
l 0 0 0 0 5 0 0 0 0 0 0 0 0 0
e 1 0 0 0 0 6 1 0 0 0 0 0 0 0
e 1 0 0 0 0 1 7 0 0 0 0 0 0 0
p 0 0 0 0 0 0 0 8 0 0 0 0 0 0
n 0 0 0 0 0 0 0 0 0 0 0 0 0 0
i 0 0 0 0 0 0 0 0 0 0 0 0 0 0
g 0 0 0 0 0 0 0 0 0 0 0 0 0 0
h 0 0 0 0 0 0 0 0 0 0 0 0 0 0
t 0 0 1 0 0 0 0 0 0 0 0 0 0 0
x 0 0 0 0 0 0 0 0 0 0 0 1 0 0
y 0 0 0 0 0 0 0 0 0 0 0 0 2 0
z 0 0 0 0 0 0 0 0 0 0 0 0 0 3
  • Результат eatsleep очевиден: диагональная полоса 12345678 в левом верхнем углу.
  • Результатом xyz является диагональ 123 в правом нижнем углу.
  • Результат a обозначается 1 около вершины (вторая строка, девятый столбец).
  • Результат t отображается 1 рядом с левым нижним левом.

Как насчет другого 1 слева, сверху и рядом с 6 и 7? Они не учитываются, потому что они появляются внутри прямоугольника, образованного диагональю 12345678 - другими словами, они уже покрыты eatsleep.

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

Ответ 2

Обычно этот тип подстрочного соответствия выполняется с помощью отдельной структуры данных, называемой Trie (произносится как try). Конкретным вариантом, который лучше всего подходит для этой проблемы, является дерево суффиксов . Ваш первый шаг должен состоять в том, чтобы взять ваши входы и построить дерево суффикса. Затем вам нужно будет использовать дерево суффикса, чтобы определить самую длинную общую подстроку, которая является хорошим упражнением.