Самая длинная общая подстрока без сокращения слова - PullRequest
2 голосов
/ 11 апреля 2019

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

Например: данные string1 = He had 3 pennies and 5 quarters и string2 = q3nniesp должны возвращать pennies.

Другой пример: string1 = They named the new place face cafe и string2 = e face, и результат будет e face cafe.

Я пытаюсь выяснить этот алгоритм, но не могу решить, нужно ли мне преобразовать этик массиву символов или оцените его как строку.То, как в обеих строках могут быть пробелы, сбивает меня с толку.

Я следовал за некоторыми из существующих вопросов о стековом потоке и пытался изменить этот код с https://www.geeksforgeeks.org/:

static String findLongestSubsequence(String str1, String str2) {

        char[] A = str1.toCharArray();
        char[] B = str2.toCharArray();
        if (A == null || B == null) return null;

        final int n = A.length;
        final int m = B.length;

        if (n == 0 || m == 0) return null;

        int[][] dp = new int[n+1][m+1];

        // Suppose A = a1a2..an-1an and B = b1b2..bn-1bn
        for (int i = 1; i <= n; i++ ) {
            for (int j = 1; j <= m; j++) {

                // If ends match the LCS(a1a2..an-1an, b1b2..bn-1bn) = LCS(a1a2..an-1, b1b2..bn-1) + 1
                if (A[i-1] == B[j-1]) dp[i][j] = dp[i-1][j-1] + 1;

                    // If the ends do not match the LCS of a1a2..an-1an and b1b2..bn-1bn is
                    // max( LCS(a1a2..an-1, b1b2..bn-1bn), LCS(a1a2..an-1an, b1b2..bn-1) )
                else dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);

            }
        }

        int lcsLen = dp[n][m];
        char[] lcs = new char[lcsLen];
        int index = 0;

        // Backtrack to find a LCS. We search for the cells
        // where we included an element which are those with
        // dp[i][j] != dp[i-1][j] and dp[i][j] != dp[i][j-1])
        int i = n, j = m;
        while (i >= 1 && j >= 1) {

            int v = dp[i][j];

            // The order of these may output different LCSs
            while(i > 1 && dp[i-1][j] == v) i--;
            while(j > 1 && dp[i][j-1] == v) j--;

            // Make sure there is a match before adding
            if (v > 0) lcs[lcsLen - index++ - 1] = A[i-1]; // or B[j-1];

            i--; j--;

        }

        return new String(lcs, 0, lcsLen);
    }

Но я продолжаю получать неправильные результаты.Например, первый вывод дает output = 3nnies, я действительно застрял в этой точке, кто-нибудь может дать руку или немного поцарапать алгоритм?Спасибо.

1 Ответ

1 голос
/ 11 апреля 2019

Я опробовал ваш оригинальный алгоритм, к сожалению, он был не в правильном направлении.

Я предполагаю, что применяются следующие рекомендации:

  • Соответствующая подстрока содержит символы из данной подстроки, которые могут быть не в порядке.
  • Символ из данной подстроки может появляться несколько раз в соответствующей подстроке.

Поэтому я взял на себя смелость использовать алгоритм грубой силы при использовании потоков Java:

// complexity of O(N*M), where N is the length of the original string and M is the length of the substring
static String longestCommonSequence(String string, String sub) {
    List<Character> primaryMatch = new ArrayList<>();
    List<Character> secondaryMatch = new ArrayList<>();
    // N iterations loop on original string
    for(char c : string.toCharArray()) {
      // M iterations loop on the substring
      if(sub.indexOf(c) != -1) {
        primaryMatch.add(c);
      }
      else {
        if(!primaryMatch.isEmpty()) {
          // replace secondaryMatch content with the current longet match
          if(secondaryMatch.size() <= primaryMatch.size()) {
            secondaryMatch.clear();
            secondaryMatch.addAll(primaryMatch);
          }
          primaryMatch.clear();
        }
      }
    }
    if(primaryMatch.size() < secondaryMatch.size()) {
      return secondaryMatch.stream().map(String::valueOf).collect(Collectors.joining());
    }
    return primaryMatch.stream().map(String::valueOf).collect(Collectors.joining());
}

Выходы для предоставленных вами входов:

string1 = He had 3 pennies and 5 quarters and string2 = q3nniesp ---> pennies
string1 = They named the new place face cafe and string2 = e face ---> ace face cafe 

Обратите внимание на разницу во втором выводе - на основе описанного вами поведения вывода результат вышеприведенного алгоритма является правильным, поскольку ace face cafe длиннее, чем e face cafe, так как многократное использование символов из данной подстроки разрешены.

Обратите внимание на небольшую проблему в алгоритме: if(secondaryMatch.size() <= primaryMatch.size())

Текущая реализация в случае нескольких совпадающих подстрок одинаковой длины вернет последнее совпадение (на основе порядка символов в исходной строке). Если вы хотите вернуть первое совпадение, замените <= на <.

Если описанные мной допущения недопустимы - прокомментируйте этот ответ, и я обновлю его в соответствии с вашими требованиями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...