<1.6> 중복문자제거

mutexlocking·2022년 7월 7일
0

문제
: 소문자 알파벳으로 구성된 문자열이 입력되면, 해당 문자열에서 중복된 알파벳을 제거한 문자열을 출력하기.
(단 중복이 제거된 문자열의 각 문자는 , 원래 문자열의 순서를 유지해야 함)

이 문제의 요구사항은 결국 중복된 문자를 인식할 수 있냐 이고,
이에 대한 나의 해결 로직을 아래와 같이 생각하였다.

[빈도수 배열 활용]

  • 빈도수 배열이란 0번부터 122번의 index를 갖는 int형 배열
  • 이중 97번부터 122번까지의 element만을 사용하는데,
  • 이때 각 element 값은 "해당 아스키코드의 소문자가 사용된 횟수"를 의미
    EX) "aabbcc"가 입력된다면 ,
    * 이 문자열을 순차탐색 하면서 - 빈도수 배열에 frequency[charArr[i]]++ 식으로 사용된 빈도수를 기록하면 됨
  • 이러한 빈도수 배열을 활용하기 위해서는 char 값을 ,
    배열의 index자리에 넣었을 때 "아스키코드 값"으로 인식되어야만 했는데, 다행이도 아스키코드값으로 인식되었다.

[위와 같은 빈도수 배열 활용한 해결 로직]

  • 이러한 빈도수 배열을 활용하여 입력한 문자열을 만날 때 마다 빈도수를 1씩 증가시킨다면
  • 한번도 만나지 않은 , 즉 빈도수 배열 값이 0인 문자인 경우에 대해서만 결과 배열에 담고
  • 이미 1번 이상 만난, 즉 빈도수 배열 값이 1이상인 문자인 경우는 결과배열에 담지 않는 방식으로 , 중복을 제거하면서도 순서를 유지시킬 수 있다.

이에 따른 나의 코드는 아래와 같다.

import java.util.Scanner;

public class Main {

    public static String solution(String str){

        /**
         * 핵심은 문자열을 구성하는 각 문자의 아스키코드값을 index로 하여 - frequency 배열의 각 element에 접근한다는 점!
         * 그러려면 char값이 index로 사용될 때 - 알아서 아스키코드 값으로써 인식되어야 하는데 - 다행이 그렇게 동작함!
         * */
        //0. 먼저  각 소문자 문자열 별 사용 횟수를 기록하는 frequency배열 초기화
        int[] frequency = new int[123]; // 이중에서 97부터 122까지의 element만 사용할 것

        //1. 이후 String 을 char 배열로 변환 + 결과 문자를 담을 문자열 배열도 함께 생성
        char[] charArr = str.toCharArray();
        char[] resultArr = new char[charArr.length];
        int idx = 0;



        //2. 이후 변환된 char배열을 순차탐색 하면서 , check 배열 값을 이용하여 , 한번도 사용되지 않은 문자만 순서대로 결과 배열에 담음
        for(char c : charArr){
            if(frequency[c] == 0){
                resultArr[idx++] = c;
            }

            frequency[c]++;
        }

        /**
         * 여기서 resultArr을 그냥 String으로 변환하면 , 값이 채워지지 않은 char값은 이상하게 변환되므로, 값이 채워진 char값 까지만 String으로 변환해야함
         * */
        //3. 결과 배열을 String 으로 변환하여 리턴 -> 이때 문자열에 이어붙이는 방법 사용  (비효율적이긴 함)
        String resultStr = "";
        for(int i=0; i<idx; i++){
            resultStr += resultArr[i];
        }
        return resultStr;
    }

    public static void main(String[] args){

        //0. Scanner 준비
        Scanner sc = new Scanner(System.in);

        //1. 문자열 입력
        String str = sc.next();

        //2. solution()의 인자로 넘겨 - 중복을 제거한 채 순서를 유지하는 문자열을 리턴받음
        String result = solution(str);

        //3. 리턴받은 문자열을 출력
        System.out.println(result);
    }
}

이렇게 문제를 해결한 후 강의를 들었는데, 선생님 께서는 String 클래스의 인스턴스 메서드 indexOf()를 사용하여 훨씬 더 직관적인 코드로 이 문제를 해결하셨다.

[indexOf() 메서드]
: 해당 문자열을 구성하는 각 문자에 대해서, 각 문자가 가장 처음으로 사용된 인덱스를 반환
EX) "hello".indexOf('l') -> "hello"문자열에서 'ㅣ' 문자가 가장 처음으로 사용된 인덱스인 2를 리턴

[indexOf() 메서드의 활용 방식]

  • 문자열을 구성하는 문자 중, 처음으로 사용된 문자라면 , 인덱스와 indexOf()가 반환한 인덱스 값이 일치할 것
  • 그렇지 않고 두 번째 이상으로 중복 사용된 문자라면, 인덱스 값이 indexOf()가 반환한 인덱스 값 보다 클것
    EX) "aabbcc" 의 문자열에 대해서
    - 인덱스 값은 012345
    - indexOf 값은 002244
    - 따라서 두 값을 비교하여 , 두 값이 같아서 중복 사용된게 아니라 처음 사용된 경우라면 - 결과 배열에 담는다.
    - 그렇지 않고 두 값이 다르면, 이전에 사용된 문자라는 의미이므로 결과 배열에 담지 않는다.

이러한 indexOf() 사용 방식은 결국 기존 문자의 순서를 유지해야 한다는 조건이 존재하기 때문에 사용할 수 있는 방식이지만,
덕분에 굉장히 간결한 코드로 문제를 해결할 수 있었다.

이를 활용하여 문제를 해결한 코드는 아래와 같다.

import java.util.Scanner;

public class Main2 {

    public static String solution(String str){

        String resultStr = "";

        //인자로 넘어온 문자열을 순자 탐색 하면서 -> 현재 인덱스와 - 최초로 사용된 인덱스가 일치하여
        // 현재 그 문자가 , 이 문자열에서 최초로 사용된 경우에 한하여 -> 그 문자를 결과 문자열에 담는다.

        for(int i=0; i<str.length(); i++){
            if(i == str.indexOf(str.charAt(i)))
                resultStr += str.charAt(i);
        }

        return resultStr;
    }

    public static void main(String[] args){
        //0. Scanner 준비
        Scanner sc = new Scanner(System.in);

        //1. 문자열 입력
        String str = sc.next();

        //2. solution()을 호출하여 중복을 제거한 문자열 반환
        String resultStr = solution(str);

        //3. 출력
        System.out.println(resultStr);
    }
}
profile
개발자가 되고자 try 하는중

0개의 댓글