본문 바로가기
Algorithm[Java]/문자열 처리

Reverse Words in a String [Java / LeetCode 151]

by blackjack_96 2024. 1. 25.

URL : https://leetcode.com/problems/reverse-words-in-a-string/description/

 

LeetCode - The World's Leading Online Programming Learning Platform

Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.

leetcode.com

 

오늘은 비교적 쉬운 문제이지만, 주의할 점이 몇가지 존재하는, 단어 뒤집기 문제를 풀어보도록 하겠습니다. 문제에서 요구하는 게 직관적인 편에 속하는지라 쉽게 이해할 수 있으니, 읽어보도록 할까요?

 

문제 이해

Given an input string s, reverse the order of the words. A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space. Return a string of the words in reverse order concatenated by a single space. Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should only have a single space separating the words. Do not include any extra spaces.

Example 1:
Input: s = "the sky is blue"
Output: "blue is sky the"

 

 

Example 2:
Input: s = "  hello world  "
Output: "world hello"
Explanation: Your reversed string should not contain leading or trailing spaces.

 

 

Example 3:
Input: s = "a good   example"
Output: "example good a"
Explanation: You need to reduce multiple spaces between two words to a single space in the reversed string.

 

 즉 문제에서 요구하는 것이 무엇인지 그림을 통해 알아보도록 하겠습니다.

주어진 예시 1번의 풀이

 

주어진 예시 2번의 풀이

 

 

주어진 예시 3번의 풀이

 

 주어진 예시인 1,2,3번을 통해서 어떠한 아이디어로 접근해야 하는지 떠올릴 수 있습니다. 주어진 문자열을 하나 이상의 공백 단위로 분리해서 순수하게 영문자로만 이루어진 단어를 추출해야 하므로, 자바의 String 객체의 split메소드를 이용해서 풀면 되겠다는 생각이 듭니다!

 

split 메소드의 함정 : split(" ")

 주어진 문자열에서 순수하게 영문자로만 이루어진 단어를 추출하기 위해서 split메소드를 이용하면 좋을 것 같다는 생각까지 하게 되었습니다. 그런데 이 split()을 어떻게 사용하느냐에 따라서 문제 풀이의 성공 여부가 갈립니다. 먼저 다음과 같은 문자열이 주어졌을 때에 split(" ")을 통해서 순수한 단어를 추출해 볼까요?

 

길이 1의 공백만 존재하는 경우에는 우리의 예상대로 잘 동작한다

 

  문자열이 주어져 있고, 문자열의 단어 사이 사이에 단 하나의 공백만이 존재하는 경우에는, 우리가 예상했던 대로 잘 단어 추출이 이루어 집니다. 하지만 다음 경우는 어떨까요?

길이 2 이상의 공백이 존재할 경우, split(" ")는 우리의 예상과 다르게 동작한다

 

 split(" ")메소드를 사용해서 순수한 영문자로 이루어진 단어 단위로, 즉 "hello"와 "world"로 분리되기를 바랬는데 위와 같은 결과가 나와버렸습니다. 왜 그런지 자세히 들여다 보면,

 

길이 4 공백이 포함된 문자열에서의 split(" ")동작

 

 split(" ")이란 메소드를 호출하면 공백 단위로 단어들을 추출해 내는데,위에서 보시다시피 공백과 공백 사이에는 아무런 문자열이 존재하지 않아서(세로로 길쭉한 빨간 직사각형), 빈 문자열인 "" 가 반환이 되었던 것입니다. 그러면 어떻게 해야 할까요? 우리의 목표는, 영문자로 된 순수한 단어를 추출하는 것입니다. 즉 하나 공백 뿐 아니라 길이 2 이상의 공백 단위로도 문자열을 분리할 수 있어야 합니다.

 

 이 문제를 푸는 열쇠는 바로 정규표현식(Regular Expression)을 사용하는 것입니다. 다음과 같이 말입니다.

 

 split("\\s+")

하나 이상의 공백이 연달아 나오는 경우를 의미하는 정규표현식

 

 split메소드의 이러한 정규표현식을 인자로 넣어주고 호출하게 되면, 공백이 두 개 이상 연달아 등장하는 문자열 에서도, 영문자로 이루어진 순수한 단어를 추출해낼 수가 있습니다.

정규표현식을 이용한 split메소드의 호출 결과 및 동작 원리

 

strip()을 이용해 양쪽 끝의 공백을 제거하는 작업의 필요성

 그럼 위와 같이 하나 이상의 연속된 공백이 포함된 문자열에서 단어를 순수하게 추출해 내기 위해서는 split("\\s+")라는 도구를 사용해야 한다는 것까지 결론이 났습니다. 하지만 이게 전부일까요? 아쉽게도, 하나의 작업이 더 필요합니다! 이 작업이 무엇인지 설명하기 위해서, 다음 예를 한번 살펴보겠습니다.

 

양 끝에도 공백문자가 등장하는 경우에서의 split메서드 동작

 

 이렇듯 양 끝에도 하나 이상의 공백문자가 등장하는 경우에, 우리가 원하는 대로 동작하지 않습니다. 그래서 주어진 문자열의 양 끝에 공백문자를 제거해서 새로운 String인스턴스를 반환 해주는 메서드인 strip() 메서드를 이용해야만 합니다! 즉, 다음 그림과 같은 순서로 해야 우리가 원하는 대로 순수하게 영문자로만 이루어진 영단어를 추출해 낼 수가 있는 것입니다.

우리가 원하는 대로 동작하기 위해서는 strip()메서드를 먼저 호출해야 한다

 

하나의 코드 스니펫으로!

 그래서 결론적으로, 하나 이상의 공백이 연달아 등장하는, 그것도 문자열의 양 끝에 존재하는 경우에서 순수하게 단어만을 추출하고 싶다면 다음과 같이 코드를 짜면 됩니다.

// 먼저 strip()으로 양 끝의 공백문자를 제거한 후
// split("\\s+") 호출
String[] words = s.strip().split("\\s+")

 

 사실 이렇게, 공백이 등장하는 문자열에서 순수하게 단어만을 추출해내는 경우는 많으니까, 위와 같은 로직을 되도록이면 암기하고 숙지해서 나만의 코드 스니펫(Code Sniffet)으로 만드는 것을 추천 드립니다! 그래야만 빨리, 효율적으로 코딩을 할 수 있을 테니까요

 

 자, 사실 이번 문제는 여기까지만 알면 매우매우 쉽게 풀 수 있습니다.ㅎㅎ 바로 전체 소스코드부터 보고 설명을 들어가겠습니다.

 

전체 소스코드

 

class Solution {
    public String reverseWords(String s) {
        StringBuilder answer = new StringBuilder("");
        
        // 순수하게 단어만을 추출하는 부분
        String[] words = s.strip().split("\\s+");

	// 단어들을 추출해서 역순으로 붙인다
        for(int i=words.length-1; i >= 0; i--) {
            answer.append(words[i]);
            answer.append(" ");
        }

        // 맨 마지막 단어와 함께 포함되서 들어온 공백문자 하나를 제거하고 반환!
        return answer.toString().substring(0, answer.length()-1);
    }
}

 

정답 도출 과정

 

 위 소스코드를 통해 주어진 예시가 어떻게 정답으로 처리되는가를 나타낸 그림입니다. 그림을 보시면 쉽게 이해하실 수 있을 것이라고 생각합니다.

 

감사합니다.