Как проверить, что строка содержит уникальные символы внутри цикла?
Что проверяют
Базовое владение коллекциями и умение улучшить очевидное решение: ранний выход, выбор структуры под алфавит, оценка сложности. Следом обычно просят решить без дополнительной памяти или спрашивают, что будет со строкой из эмодзи.
Как отвечать
Основное решение за O(n) с HashSet и выходом на первом повторе:
Set<Character> seen = new HashSet<>();
for (char c : s.toCharArray()) {
if (!seen.add(c)) return false;
}
return true;add возвращает false, если элемент уже есть, поэтому отдельный contains не нужен, и на символ приходится один поиск вместо двух. Дальше варианты, которые стоит назвать:
- ранний выход по длине: если алфавит известен, например ASCII из 128 символов, строка длиннее алфавита заведомо содержит повтор;
- массив вместо множества:
boolean[128]для ASCII илиboolean[65536]для любогоchar. Нет упаковкиcharвCharacterи хэширования, поэтому на практике быстрее; - битовая маска: для строчных латинских букв хватит одного
int, проверка(mask & (1 << (c - 'a'))) != 0; - без дополнительной памяти: вложенный цикл за O(n²) или сортировка массива символов за O(n log n) и сравнение соседей. Учтите, что строка в Java неизменяемая и
toCharArray()всё равно создаёт копию; - через стримы:
s.chars().distinct().count() == s.length()короче, но проходит всю строку без раннего выхода.
Уточните у интервьюера, различается ли регистр и что считается символом. char в Java это кодовая единица UTF-16, и эмодзи занимает два char. Для корректной работы с Unicode перебирайте s.codePoints() и храните Set<Integer>.
Где спотыкаются
Ищут повтор через List.contains или indexOf внутри цикла и получают O(n²), не замечая этого.
Не обрабатывают null и пустую строку: пустая строка должна давать true, а null лучше явно отклонить.
Забывают про суррогатные пары и считают, что длина строки равна числу видимых символов.