Какие знаешь способы поиска последнего слова в строке?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Ответ
Поиск последнего слова в строке — простая, но часто задаваемая задача на собеседованиях. Давайте разберем различные подходы и их сложность.
Способ 1: Разделение по пробелам (split)
Самый простой и читаемый подход.
public String getLastWord(String str) {
if (str == null || str.trim().isEmpty()) {
return "";
}
String[] words = str.trim().split("\\s+");
return words[words.length - 1];
}
// Примеры
System.out.println(getLastWord("Hello World Java")); // Java
System.out.println(getLastWord(" Spaces ")); // Spaces
System.out.println(getLastWord("SingleWord")); // SingleWord
System.out.println(getLastWord(" ")); // ""
Преимущества:
- Простой и понятный код
- Обрабатывает многочисленные пробелы
Недостатки:
- O(n) временная сложность (нужно создать массив)
- O(n) память (весь массив в памяти)
- Неэффективно для больших строк
Сложность: O(n) время, O(n) память
Способ 2: Поиск с конца (lastIndexOf)
Мер эффективнее — ищем с конца.
public String getLastWord(String str) {
if (str == null || str.trim().isEmpty()) {
return "";
}
str = str.trim(); // удаляем пробелы с начала/конца
int lastSpaceIndex = str.lastIndexOf(" ");
if (lastSpaceIndex == -1) {
return str; // одно слово
}
return str.substring(lastSpaceIndex + 1);
}
// Примеры
System.out.println(getLastWord("Hello World Java")); // Java
System.out.println(getLastWord("SingleWord")); // SingleWord
Преимущества:
- O(n) время, но на практике может быть быстрее
- O(1) память (только substring)
- Логичный подход
Недостатки:
- Требует trim() перед обработкой
- Может быть неэффективно для строк с пробелами в конце
Сложность: O(n) время, O(k) память (где k — длина последнего слова)
Способ 3: Итерация с конца (reverse iteration)
Итерируем строку с конца — может быть быстрее на практике.
public String getLastWord(String str) {
if (str == null || str.isEmpty()) {
return "";
}
int end = str.length() - 1;
// Пропускаем пробелы в конце
while (end >= 0 && Character.isWhitespace(str.charAt(end))) {
end--;
}
if (end < 0) {
return ""; // строка только из пробелов
}
// Ищем начало слова
int start = end;
while (start > 0 && !Character.isWhitespace(str.charAt(start - 1))) {
start--;
}
return str.substring(start, end + 1);
}
// Примеры
System.out.println(getLastWord("Hello World Java")); // Java
System.out.println(getLastWord(" Spaces ")); // Spaces
System.out.println(getLastWord("a b c")); // c
Преимущества:
- O(k + m) сложность, где k — расстояние до слова, m — длина слова
- O(m) память (только слово)
- На практике быстрее, если слово близко к концу
Недостатки:
- Сложнее читать
- Много граничных условий
Сложность: O(n) худший случай, O(m) лучший случай
Способ 4: Регулярные выражения (regex)
Для сложных определений "слова".
public String getLastWord(String str) {
if (str == null || str.isEmpty()) {
return "";
}
// Последовательность символов, не являющихся пробелами
Pattern pattern = Pattern.compile("\\S+$");
Matcher matcher = pattern.matcher(str);
if (matcher.find()) {
return matcher.group();
}
return "";
}
// Примеры
System.out.println(getLastWord("Hello World Java")); // Java
System.out.println(getLastWord(" test ")); // test
Преимущества:
- Гибкий (легко менять определение "слова")
- Поддерживает сложные паттерны
Недостатки:
- Медленнее других подходов (компиляция regex)
- Избыточно для простого случая
- O(n) память для компиляции
Сложность: O(n) время, O(n) память
Способ 5: Apache Commons Lang
import org.apache.commons.lang3.StringUtils;
public String getLastWord(String str) {
if (StringUtils.isEmpty(str)) {
return "";
}
String trimmed = StringUtils.trim(str);
return StringUtils.substringAfterLast(trimmed, " ");
}
// или используя getWords
public String getLastWord(String str) {
String[] words = StringUtils.split(str);
return words.length > 0 ? words[words.length - 1] : "";
}
Преимущества:
- Проверенный код
- Хорошая производительность
- Читаемо
Недостатки:
- Требует внешней библиотеки
- Возможно избыточно для простой задачи
Способ 6: Stream API (Java 8+)
public String getLastWord(String str) {
if (str == null || str.isEmpty()) {
return "";
}
return Arrays.stream(str.split("\\s+"))
.filter(s -> !s.isEmpty())
.reduce((first, second) -> second)
.orElse("");
}
// или более элегантно
public String getLastWordStream(String str) {
return Arrays.stream(str.trim().split("\\s+"))
.reduce((first, second) -> second)
.orElse("");
}
Преимущества:
- Функциональный стиль
- Выразительный
Недостатки:
- Немного медленнее (overhead от Stream API)
- Все еще O(n) память для массива
Сложность: O(n) время, O(n) память
Сравнение производительности
| Метод | Время | Память | Читаемость | Когда использовать |
|---|---|---|---|---|
| split() | Средне | O(n) | Отлично | Простые случаи |
| lastIndexOf() | Средне | O(k) | Хорошо | Обычно |
| Reverse iteration | Быстро | O(k) | Плохо | Performance critical |
| Regex | Медленно | O(n) | Хорошо | Сложные паттерны |
| Stream API | Средне | O(n) | Отлично | Modern Java |
Рекомендации
Для собеседования: используй способ 3 (reverse iteration) — показывает понимание сложности и оптимизации.
Для production: используй способ 1 (split) или Stream API — простота и читаемость важнее, чем микро-оптимизации.
Для микро-сервисов: используй способ 2 (lastIndexOf) — сбалансированный подход.
// Готовое решение для production
public class StringUtils {
public static String getLastWord(String str) {
if (str == null || str.trim().isEmpty()) {
return "";
}
String trimmed = str.trim();
int lastSpace = trimmed.lastIndexOf(' ');
return lastSpace == -1 ? trimmed : trimmed.substring(lastSpace + 1);
}
}
Главный вывод: выбирай подход в зависимости от требований: простота vs производительность vs гибкость.