개발자는 기록이 답이다

[백준/BOJ][Java][9086]문자열 본문

알고리즘/백준

[백준/BOJ][Java][9086]문자열

slow-walker 2023. 8. 24. 11:55

2023.08.21 - [Java] - Java, String의 constant pool과 Heap의 차이

 

Java, String의 constant pool과 Heap의 차이

Java String 문자열? 순서를 가진 문자들의 집합 “쌍따옴표를 통해 나타낼 수 있음” 글자, 단어, 문장, 문서 등 문자로 구성된 자료형 // 기본 자료형 int var_integer = 10; double var_real - 3.141592; char var_ch

strong-park.tistory.com


 

https://www.acmicpc.net/problem/9086

 

9086번: 문자열

입력의 첫 줄에는 테스트 케이스의 개수 T(1 ≤ T ≤ 10)가 주어진다. 각 테스트 케이스는 한 줄에 하나의 문자열이 주어진다. 문자열은 알파벳 A~Z 대문자로 이루어지며 알파벳 사이에 공백은 없으

www.acmicpc.net

내가 푼 풀이

풀고나서 문제점들이 많이 보여서 어떤 거 였는지 적어보력 한다. 💩

1. 안해도 되는데 길이가 1일때를 굳이 분기처리했습니다.

- 어차피 "0"으로 들어온 문자열의 첫번째 인덱스와 마지막 인덱스를 붙이면 "00"이 되는데, 분기처리를 해버렸다.

2. 문자열 분리와 결합으로 성능적으로 봤을 때 메모리,시간, 코드길이가 전부 좋지 않았다.

- 나는 입력값을 하나씩 받으려고 sc.next();를 3번써서 저렇게 한건데, 다른 사람들 코드보니까 입력 한번에 받고 For문 안에서 처리했다.

- 입력된 세 개의 문자열을 합치고, 문자열을 분리해서 배열에 저장하는 부분이 성능적으로 비효율적이다.

- 문자열을 분리하는 과정에서 정규식이나 split 함수를 사용하는 것은 문자열의 길이에 따라 성능이 크게 영향을 받을 수 있다.

- 또한 문자열 결합은 문자열의 크기에 따라 성능 저하를 유발할 수 있다.

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();
        String str1 = sc.next();
        String str2 = sc.next();
        String str3 = sc.next();

        String ans = "";

        String total = str1+ "," + str2 + ","+ str3;
        String[] strSplit = total.split(",");
        for (int i = 0; i < num; i++) {
            if (strSplit[i].length() == 1) {
                String s = String.valueOf(strSplit[i].charAt(0));
                ans = s + s;
            }
            else {
                String start = String.valueOf(strSplit[i].charAt(0));
                String end = String.valueOf(strSplit[i].charAt(strSplit[i].length()-1));
                ans = start + end;
            }
            System.out.println(ans);
        }
    }
}
메모리 시간 언어 코드 길이
18504MB
236ms Java11 870B
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();
        String str1 = sc.next();
        String str2 = sc.next();
        String str3 = sc.next();

        String ans = "";

        String total = str1 + "," + str2 + "," + str3;
        String[] strSplit = total.split(",");
        for (int i = 0; i < num; i++) {
                String start = String.valueOf(strSplit[i].charAt(0));
                String end = String.valueOf(strSplit[i].charAt(strSplit[i].length()-1));
                ans = start + end;
            System.out.println(ans);
        }
    }
}

 

메모리 시간 언어 코드 길이
18600MB 236ms Java11 684B

 

 

다른 사람 풀이 1

 

String으로 더하기 연산해서 인스턴스를 계속 생성하는것 보다, StringBuffer, StringBuilder의 append를 쓰는게 훨씬 좋은것 같다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int t = Integer.parseInt( br.readLine() );
		StringBuffer sb = new StringBuffer("");
		for(int i=0; i<t; i++) {
			String str = br.readLine();
			sb.append( str.charAt( 0 )+"" + str.charAt( str.length()-1 )+"" + "\n");
		}
		System.out.print( sb.toString() );
	}
}
메모리 시간 언어 코드 길이
12776MB 64ms Java8 512B

다른 사람 풀이 2 (static 사용에 대한 GPT풀이)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

// [백준] 9086. 문자열 (Java)
public class Main {

    static int t;
    static StringBuilder sb;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        t = Integer.parseInt(br.readLine());
        sb = new StringBuilder();

        for(int i = 0; i < t; i++){
            String str = br.readLine();

            sb.append(str.charAt(0)).append(str.charAt(str.length() - 1)).append("\n");
        }
        System.out.println(sb);
    }
}
메모리 시간 언어 코드 길이
11300MB 68ms Java8 639B

여러가지 측면에서 static을 사용했을 때 성능이 향상될 수 있습니다:

  1. 메모리 사용량 감소: 인스턴스 변수를 생성하지 않고 static 변수를 사용하면 객체마다 인스턴스 변수를 할당하는 비용이 들지 않습니다. 따라서 객체마다 중복된 데이터를 가지지 않고 하나의 변수를 여러 객체에서 공유하므로 메모리 사용량이 감소하게 됩니다.
  2. 속도 향상: static 메서드는 인스턴스를 생성하지 않고 바로 호출할 수 있기 때문에, 메서드 호출 시간이 더 짧을 수 있습니다.

그러나 성능 향상을 위해 static을 무작정 사용하는 것은 좋은 방법은 아닙니다. static 변수나 메서드는 공유되는 특성을 가지므로, 데이터의 무결성을 유지하고 의도치 않은 부작용을 피하기 위해 신중하게 사용해야 합니다.

제공해주신 코드에서 static을 사용한 부분은 성능에 직접적인 영향을 주진 않습니다. 이 코드에서는 static을 사용하여 t와 sb 변수를 클래스 수준에서 선언하고 사용하였는데, 이는 반복문 내에서 인스턴스를 생성하지 않고 공유하는 용도로 사용한 것입니다. 이런 경우 메모리 사용량이나 속도 면에서 약간의 이점이 있을 수 있습니다.

 

다른 사람 풀이 3

문자열 분리와 결합 대신에 배열이나 리스트를 사용하고, 반복문 내에서 문자열 연산을 최소화하는 방법을 고려했다. 배열을 사용하되 이렇게 for문 내에서 입력값을 받아주면 좋은 것 같다.

 

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
	public static void main(String[] args) throws Exception {
		
		BufferedReader br = new BufferedReader( new InputStreamReader(System.in ) );
		
		int t = Integer.parseInt( br.readLine() );
		String[] array = new String[ t ];
		
		for( int i=0; i<t; i++ ) {
			array[i] = br.readLine();
		}
		
		for( int i=0; i<t; i++ ) {
			System.out.println( array[i].charAt(0) + "" + array[i].charAt( array[i].length() - 1) );
		}
	}
}
메모리 시간 언어 코드 길이
11308MB 68ms Java8 508B

 

 

응용 풀이

- 같은 코드인데도 불구하고 Scanner 대신 BufferedReader를 쓰는 것만으로도 성능이 확 좋아지는것 같다. 다음 코드부터는 BufferedReader를 사용해야겠다.

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();
        String[] strings = new String[num];

        for (int i = 0; i < num; i++) {
            strings[i] = sc.next();
        }

        for (int i = 0; i < num; i++) {
            String start = String.valueOf(strings[i].charAt(0));
            String end = String.valueOf(strings[i].charAt(strings[i].length() - 1));
            System.out.println(start + end);
        }
    }
}
메모리 시간 언어 코드 길이
17556MB
216ms Java11 553B
import java.io.BufferedReader;
import java.io.InputStreamReader;


public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader( new InputStreamReader(System.in ) );
        
        int num = Integer.parseInt(br.readLine());
        String[] strings = new String[num];

        for (int i = 0; i < num; i++) {
            strings[i] = br.readLine();
        }

        for (int i = 0; i < num; i++) {
            String start = String.valueOf(strings[i].charAt(0));
            String end = String.valueOf(strings[i].charAt(strings[i].length() - 1));
            System.out.println(start + end);
        }
    }
}
메모리 시간 언어 코드 길이
14072MB 124ms Java11 683B

 

 

와 그리고 바로 위에 똑같은 코드를 자바버전을 11이 아닌 8로 하니까 속도가 엄청빠르다...왜 그럴까?

메모리 시간 언어 코드 길이
11300MB 76ms Java8 683B

 

출력 속도를 비교한 아래 백준 사이트를 참고해보자

 

https://www.acmicpc.net/blog/view/57

 

출력 속도 비교

여러가지 언어와 출력 방법을 이용해서 시간이 얼마나 걸리는지 비교해 보았습니다. 방법: 총 N개의 줄에 1부터 10,000,000까지의 자연수를 한 줄에 하나씩 출력하는 시간을 측정. 10번 측정해서 평

www.acmicpc.net