Найти самую длинную подстроку без повторяющихся символов

Найти самую длинную подстроку без повторяющихся символов

1. обзор

В этом руководстве сравниваются способы поиска самой длинной подстроки уникальных букв с использованием Java. Например, самая длинная подстрока уникальных букв в «CODINGISAWESOME» - это «NGISAWE».

2. Метод грубой силы

Начнем с наивного подхода. Начнем сwe can examine each substring whether it contains unique characters:

String getUniqueCharacterSubstringBruteForce(String input) {
    String output = "";
    for (int start = 0; start < input.length(); start++) {
        Set visited = new HashSet<>();
        int end = start;
        for (; end < input.length(); end++) {
            char currChar = input.charAt(end);
            if (visited.contains(currChar)) {
                break;
            } else {
                visited.add(currChar);
            }
        }
        if (output.length() < end - start + 1) {
            output = input.substring(start, end);
        }
    }
    return output;
}

Поскольку возможных подстрокn*(n+1)/2,the time complexity of this approach is O(n^2).

3. Оптимизированный подход

Теперь давайте посмотрим на оптимизированный подход. Мы начинаем обход строки слева направо и отслеживаем:

  1. текущая подстрока с неповторяющимися символами с помощью индексаstart иend

  2. самая длинная неповторяющаяся подстрокаoutput

  3. таблица поиска уже символовvisited

String getUniqueCharacterSubstring(String input) {
    Map visited = new HashMap<>();
    String output = "";
    for (int start = 0, end = 0; end < input.length(); end++) {
        char currChar = input.charAt(end);
        if (visited.containsKey(currChar)) {
            start = Math.max(visited.get(currChar)+1, start);
        }
        if (output.length() < end - start + 1) {
            output = input.substring(start, end + 1);
        }
        visited.put(currChar, end);
    }
    return output;
}

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

Поскольку мы обходим строку только один раз,the time complexity will be linear, or O(n).

Этот подход также известен какsliding window pattern.

4. тестирование

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

@Test
void givenString_whenGetUniqueCharacterSubstringCalled_thenResultFoundAsExpected() {
    assertEquals("", getUniqueCharacterSubstring(""));
    assertEquals("A", getUniqueCharacterSubstring("A"));
    assertEquals("ABCDEF", getUniqueCharacterSubstring("AABCDEF"));
    assertEquals("ABCDEF", getUniqueCharacterSubstring("ABCDEFF"));
    assertEquals("NGISAWE", getUniqueCharacterSubstring("CODINGISAWESOME"));
    assertEquals("be coding", getUniqueCharacterSubstring("always be coding"));
}

Здесь мы пробуем иtest boundary conditions as well as the more typical use cases.

5. Заключение

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

И, как всегда, доступен исходный кодover on GitHub.