Java - 알고리즘

백준 Java - 문자열 단계(1) : 11720번, 10809번 , 2675번, 1157번

TerianP 2021. 11. 2.
728x90

이번에는 글쓰는 주기가 살짝 늦었네요ㅠㅠ반성하겠습니다...

 

여튼 오늘도 자바 백준문제 시작합니다! 오늘부터는 문자열 단계 문제입니다.

 

1. 11720번 : 숫자의 합 구하기

N개의 숫자가 공백 없이 쓰여있다. 이 숫자를 모두 합해서 출력하는 프로그램을 작성하라.

	static void Q_11720() throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

		int num = Integer.parseInt(br.readLine()); // 첫번재 숫자 입력
		char[] ch = br.readLine().toCharArray(); // 두번째 숫자 입력

		int result = 0;

		for(int i=0; i<num; i++) {
			result+=(ch[i]-'0');
		}
		bw.write(result+"\n");
		br.close();
		bw.flush();
		bw.close();
	}

이 문제는 간단합니다. 정말 간단해요!

1) 첫 번째로 받은 숫자만큼 다음으로 오는 문자들을 더해주면 됩니다.

2) 이때 중요한 것이 사실 두번째로 오는 숫자는 100개가 오던 1000개가 오던 상관없이 첫번째로 입력받은 숫자를 기준으로만 더해주면 됩니다.

3) 1번 2번을 활용해서 첫번째 숫자 num 을 입력받았고, 다음으로 두번째 숫자를 char 타입으로 입력받아 배열로 묶었습니다. 최종적으로 result 에 char 배열의 값을 하나하나 더해주면 끝!!

 

 

2. 10809번 : 알파벳 찾기!

알파벳 소문자로만 이루어진 단어 S가 주어진다. 각각의 알파벳에 대해서, 단어에 포함되어 있는 경우에는 처음 등장하는 위치를, 포함되어 있지 않은 경우에는 -1을 출력하는 프로그램을 작성하시오.

	static void Q_10809() throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

		char[] ch = br.readLine().toCharArray();
		int[] num = new int[ch.length];
		int[] alphabet = new int[26];
		int[] result = new int[26];
		int a = 49;

		for(int i=0; i<26; i++) {
			result[i] = -1;
			alphabet[i] = a;
			a++;
		}

		//bw.write(Arrays.toString(alphabet)+"\n");

		for(int j=0; j<ch.length; j++) {
			num[j] = ch[j]-'0';
		}


		for(int j=0; j<ch.length; j++) {

			for(int k=0; k<alphabet.length; k++) {
				if(num[j] == alphabet[k]) {
					result[k] = j;
				}
			}

			for(int k=j+1; k<ch.length; k++) {
				if(num[j]==num[k]) {
					num[k] = -1;
				}
			}
		}


		for(int r : result) {
			bw.write(r+" ");
		}

		br.close();
		bw.flush();
		bw.close();
	}

이 문제는 솔직히 조금 어려웠습니다...입력받은 알파벳을 확인하고, 해당 알파벳이 있는지 없는지 검사하고, 있으면 알파벳 순서상의 해당 위치를, 최종적으로 3단계를 거쳐야하는데 이를 코드로 만드는데 솔직히 조금 걸렸던 거 같습니다.

 

이에 저는 아래 순서대로 풀이를 세웠습니다. 근데 굉장히 기상천외하기에ㅋㅋㅋ정해를 아니라고 생각합니다.

1) 알파벳 배열 생성 alphabet, 입력받은 알파벳을 숫자로 바꾸기 위한 num 배열, 최종 배열 result 

		char[] ch = br.readLine().toCharArray(); // 입력받은 값을 char 배열로 저장
		int[] num = new int[ch.length]; // 해당 문자가 몇번이나 나왔는지를 알기위한 배열
		int[] alphabet = new int[26]; // 알파벳 배열
		int[] result = new int[26]; // 최종 배열
		int a = 49; // 알파벳 소문자 a = 49

2) 처음 result 배열에는 -1 로 꽉 채우고, alphabet 배열에는 a부터 z 까지 숫자로 변환해서 채워줍니다.

그럼 alphabet 배열의 내용은 아래와 같ㅇ ㅣ저장됩니다.

alphabet[] = {49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74}

 

3) 다음으로 num 배열에 입력받은 알파벳을 숫자로 변환하여 저장합니다.

만약 baekjoon 이라는 문자를 받았다면, 아래와 같이 저장됩니다.

num[] = {50, 49, 53, 59, 58, 63, 63, 62}

 

4) 이제는 alphabet 과 num 배열을 비교해서 해당 num이 alphabet에서 처음 나온 위치를 result에 넣어주면 됩니다.

		for(int j=0; j<ch.length; j++) {

			for(int k=0; k<alphabet.length; k++) {
				if(num[j] == alphabet[k]) {
					result[k] = j;
				}
			}

			for(int k=j+1; k<ch.length; k++) {
				if(num[j]==num[k]) {
					num[k] = -1;
				}
			}
		}
  • 여기서 중요한 것은 2중 for 문을 사용하는 것이고, 첫번째 for 문에서의 j 는 입력받은 char 배열의 길이만큼 반복되고, 안쪽 for 문은 alphabet 의 길이만큼 반복됩니다.
  • 이때 num[j] 와 alphabet[k] 를 비교해서 result 배열의 k 번째에 num 배열의 j 값을 넣어줍니다. 이게 말로하면...뭔가 어려운데, 이렇게 생각하시면 됩니다.
  • 입력받은 값이 baek 라면 num 배열에는 [50, 49, 53, 59] 가 저장됩니다. num[0] = 50, num[1] = 49 이런 식입니다.
  • 우리는 ch 배열의 가장 첫번째 값인 b 부터 생각해야합니다. b는 int형으로 50이고, 입력받은 문자에서 가장 처음!! 가장 첫번째로 등장하였습니다. num 배열의 50 과 alphabet 배열의 50 에 대해서 비교 분석을 해야하는 것입니다.
  • num[0(j)] = 50 일 때 alphabet 배열에서는 alphabet[1(k)] = 50 이 됩니다. 이에 result[1(k)] = 0(j) 이 됩니다. 이 의미는 b 는 알파벳에서는 2번째(배열로는 1)에 위치해있고, 입력받은 값으로는 가장 첫번째(0)에 있다는 것입니다. 
  • 다음으로 a 는 알파벳으로 1번째(배열로는 0)에 위치해있고, 입력받은 값으로는 2번째 위치에 있어, 최종적으로는 result 값에는 0번째 위치에 1을 적어주는 것입니다.
  • 마지막에 나오는 for 문은 중복값을 없애주기 위해서 입니다. joon 같은 경우 o 가 2번 나오는데, 우리가 원하는 것은 '처음 등장한 자리' 이기 때문에 o 자리에 2 가 아닌 1을 주어야 합니다. 이에 num 값을 비교하면서 서로 같은 값을 가진다면 그 중 하나는 -1로 변환되게 만들었습니다.

 

이건 제가 진짜 웃기게 푼거라서...설명하는게 오히려 더 어렵네요. 다른분들이 푼거보면 훨씬 더 짧고 간단한 방법도 있습니다. 이 글은 이런 방법도 있구나...정도로만 알아두셔도 좋을 것 같습니다.

 

3. 2675번 : 문자열 반복!

문자열 S를 입력받은 후에, 각 문자를 R번 반복해 새 문자열 P를 만든 후 출력하는 프로그램을 작성하시오. 즉, 첫 번째 문자를 R번 반복하고, 두 번째 문자를 R번 반복하는 식으로 P를 만들면 된다. S에는 QR Code "alphanumeric" 문자만 들어있다.

	static void Q_2675() throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

		int t_case = Integer.parseInt(br.readLine()); // 전체 테스트 케이스
		String[] str = new String[t_case]; // 입력받는 문자열을 배열로 저장

		
		for(int i=0; i<t_case; i++) { // 문자열을 string 배열로 저장
			str[i] = br.readLine();
		}
		
		
		for(int i=0; i<str.length; i++) { // 배열의 각 인덱스 값을 char 로 짤라서 저장
			char[] result = str[i].toCharArray();
			
			for(int j=2; j<result.length; j++) {
				for(int k=0; k<(result[0]-'0'); k++) {
					bw.write(result[j]); // 출력
				}
			}
			bw.newLine();
			bw.flush();
		}
				
		br.close();
		bw.flush();
		bw.close();
	}

백준 문제는 참으로 웃긴게 하나는 뭔가 어려워서 '내가 바보구나 똥멍청이구나ㅠㅠ' 하고 있으면 다음 문제는 뭔가 쉬운 문제를 던저주더라구요ㅋㅋ

이번에도 이전 문제와 비교하면 훨씬 쉬운 문제입니다.

1) 이 문제에서 중요한 것은 입력받은 문자들 중에서 가장 첫 숫자만큼 입력받은 문자들을 반복! 하는것입니다. 여기서 대충 느낌이 오셨겠지만 배열로 잘라서 반복하면 되는 문제입니다.

2) 뭔 말이냐 하면, 첫번째 입력받은 숫자만큼 테스트 케이스를 만들어야 하고(반복), 다음으로 입력받은 문자들 중에서 첫번째 숫자만큼 해당 문자들을 반복해줘야 한다는 것입니다.

 

for(int i=0; i<t_case; i++) { // 문자열을 string 배열로 저장
	str[i] = br.readLine();
	}
		
		
	for(int i=0; i<str.length; i++) { // 배열의 각 인덱스 값을 char 로 짤라서 저장
		char[] result = str[i].toCharArray();
			
		for(int j=2; j<result.length; j++) {
			for(int k=0; k<(result[0]-'0'); k++) {
				bw.write(result[j]); // 출력
				}
			}
		bw.newLine();
		bw.flush();
	}
  • 이 부분만 설명하면 될 것 같은데, 먼저 입력받은 t_case 만큼 다시 문자를 입력받습니다. 이때 문자들은 str 라는 이름의 배열에 String 타입으로 저장합니다
  • 다음으로 for 문을 사용해서 result라는 배열에 str 의 각 인덱스 값을 불러와 잘라서 char 타입으로 저장합니다.
  • 그 다음 result 배열의 2번째 값부터 result[0] 값만큼 각 문자를 반복해서 출력합니다. 이는 result 배열의 맨 앞 값만큼 다음 문자들을 반복해야하기 때문입니다.
  • 이때 2번째 값부터 반복하는 이유는 테스트 케이스를 입력받을 때 중간에 빈칸을 포함해서 입력받기 때문에 toCharray() 로 나눌때도 배열에 빈칸이 포함되기 때문입니다. 이에 배열의 공백 부분을 무시하고 넘어가기위해 2번째 부터 사용합니다.

요렇게 공백이 생겨 공백을 무시하기 위해 인덱스 2부터 시작

 

이상 끝!

 

4. 1157번 : 단어공부

알파벳 대소문자로 된 단어가 주어지면, 이 단어에서 가장 많이 사용된 알파벳이 무엇인지 알아내는 프로그램을 작성하시오. 단, 대문자와 소문자를 구분하지 않는다.

	static void Q_1157() throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

		char[] cha = br.readLine().toCharArray(); // 입력받은 값 cha 배열로 나눠 저장
		int[] tmp = new int[cha.length]; // tmp 배열 생성
		
		int[] alphabet = new int[26]; // 알파벳 배열 생성
		int[] num = new int[26]; // 
		int result=0; // 결과
		int max = 0; // 최댓값 확인
		
		for(int i=0; i<26; i++) { // 소문자는 49 부터 시작 
			alphabet[i] = ('a'-'0')+i; // 알파벳 배열에 알파벳을 아스키 코드 숫자로 변환하여 저장
		}
		
		
		for(int j=0; j<cha.length; j++) {
			if(cha[j]-'0'>=49) { // cha 배열 확인해서 49 이상이면 그대로 숫자로 tmp 배열에 저장
				tmp[j] = cha[j]-'0';
			}
			else if(cha[j]-'0'<49) { // 49 이하면, 즉 대문자인 경우 소문자로 바꿔서 tmp 배열에 저장
				tmp[j] = ((cha[j]-'0')+32);
			}
			
			for(int k=0; k<alphabet.length; k++) {
				if(tmp[j] == alphabet[k]) { 
					// int형으로 저장된 알파벳 배열과 입력받은 int 문자를 비교하여 만약 tmp의 j번째 인덱스에 있는 값과
					// 알파벳의 값이 같다면 num 배열의 k 번째 인덱스에 +1 
					num[k]+=1;
					

				}
			}
		}
			
//		bw.write(Arrays.toString(tmp));
//		bw.newLine();
//		bw.write(Arrays.toString(alphabet));
//		bw.newLine();
//		bw.write(Arrays.toString(num));
//		bw.newLine();
//		bw.flush();

		
		for(int y=0; y<num.length; y++) { // max 값 저장
			if(max<num[y]) { 
				// num 배열에서 가장 큰 값 찾아온 후, 최댓값의 인덱스인 y 를 기억하여
				// num 배열의 y번째 위치와 동일한 알파벳의 y번째 위치의 값을 result 에 저장
				// 이를 통해서 가장 많이 나온 y 번째 알파벳을 result 에 저장가능
			
				max = num[y];
				result = alphabet[y];
			}
		}
		
		Arrays.sort(num); // 배열 오름차순 정렬
//		bw.write(Arrays.toString(num));
		

		if(num[num.length-1]==num[num.length-2]) {
			System.out.println("?");
		}else {
			System.out.println((char)((result-32)+'0'));
		}

		br.close();
		bw.flush();
		bw.close();
	
	}

이번 문제는 문자열 문제중에서 가장 많이 해맨 문제가 아닐까 생각합니다. 사실 처음부터 끝까지 어려웠던 것보다는 딱 한가지 부분만 만혀서 그거에서 많이 해맸습니다.

1) 이 문제는 단어를 입력받고, 입력받은 단어에서 가장 많이 사용된 문자를 고르고, 해당 문자를 대문자로 출력하는 문제입니다. 단 가장 많이 사용된 문자가 2개 이상이면 ? 를 출력합니다. 저는 이 부분에서 막혔었습니다.

2) 사실 "단어를 입력받고, 입력받은 단어에서 가장 많이 사용된 문자를 고르고, 해당 문자를 대문자로 출력" 까지면 이전에 나왔던 "2. 10809번 : 알파벳 찾기!" 문제에서 조금만 응용하면 정말 쉽게 풀 수 있습니다. 사실 코드도 거의 비슷하긴 합니다. 다만 저 중복값이 존재할때 라는게 개인적으로 어려웠습니다. 안타깝게도 이번에는 혼자서는 못 풀었고, 결국 친구님의 도움을 받아, 해결했습니다.

3) 풀이는 아래처럼 진행하였습니다.

 

		char[] cha = br.readLine().toCharArray(); // 입력받은 값 cha 배열로 나눠 저장
		int[] tmp = new int[cha.length]; // tmp 배열 생성
		
		int[] alphabet = new int[26]; // 알파벳 배열 생성
		int[] num = new int[26]; // 
		int result=0; // 결과
		int max = 0; // 최댓값 확인
  • 입력받은 값을 char 타입의 배열로 나누어 저장하기 위한 cha, 임시로 사용할 tmp, 알파벳 배열, num 배열 , 최댓값 max , 결과 출력을 위한 result
		for(int j=0; j<cha.length; j++) {
			if(cha[j]-'0'>=49) { // cha 배열 확인해서 49 이상이면 그대로 숫자로 tmp 배열에 저장
				tmp[j] = cha[j]-'0';
			}
			else if(cha[j]-'0'<49) { // 49 이하면, 즉 대문자인 경우 소문자로 바꿔서 tmp 배열에 저장
//				System.out.print(((cha[j]-'0')+32)+" ");
				tmp[j] = ((cha[j]-'0')+32);
			}
			
			for(int k=0; k<alphabet.length; k++) {
				if(tmp[j] == alphabet[k]) { 
					// int형으로 저장된 알파벳 배열과 입력받은 int 문자를 비교하여 만약 tmp의 j번째 인덱스에 있는 값과
					// 알파벳의 값이 같다면 num 배열의 k 번째 인덱스에 +1 
					num[k]+=1;
					

				}
			}
		}
			
		
		for(int y=0; y<num.length; y++) { // max 값 저장
			if(max<num[y]) { 
				// num 배열에서 가장 큰 값 찾아온 후, 최댓값의 인덱스인 y 를 기억하여
				// num 배열의 y번째 위치와 동일한 알파벳의 y번째 위치의 값을 result 에 저장
				// 이를 통해서 가장 많이 나온 y 번째 알파벳을 result 에 저장가능

				max = num[y];
				result = alphabet[y];
			}
		}
  • 먼저 cha 배열의 값 타입을 숫자 int 형으로 바꾸는데 이때 대문자의 경우 소문자로 변환하여 tmp 배열에 저장합니다. 여기서 좀 더 좋게 표현하려면 소문자를 대문자로 바꿔서 넣는것도 좋아보입니다. 왜냐하면 결국 최종 출력은 대문자거든요ㅠㅠ
  • 다음으로 tmp 배열에 있는 값과 alphabet 에 있는 값을 하나하나 비교하면서 tmp j번째 의 값이 alphabet k 번째  값과 동일하다면 num 배열의 k 번째 값에 +1을 반복합니다. 만약 tmp에서 k 번째 알파벳이 여러번 등장한다면 해당 num k 번째 값에 +1을 여러번 반복하게 됩니다. 이 부분은 위에 10809번과 사실상 동일합니다.
  • 세번째로 for 문을 통해서 num 배열에 있는 값 중 최대값의 위치를 찾습니다. 코드 상에서는 num 배열에서 최대값인 num[y] 가 있다면 우리는 y 값을 찾아서 alphabet[y] 번째 값을 result 에 저장합니다.

 

Arrays.sort(num) // 배열 오름차순 정렬

if(num[num.length-1]==num[num.length-2]) {
	System.out.println("?");
}else {
	System.out.println((char)((result-32)+'0'));
}
  • 최종적으로는 if 문을 통해서 출력합니다. 이때 arrays.sort 를 통해서 num 배열을 오름차순으로 정렬합니다. 이렇게하면 오름차순으로 정렬되어있기 때문에 배열의 맨 마지막 값은 최대값이 됩니다.
  • 이때 맨 마지막 값과 맨 마지막에서 하나 전 값과 비교했을때 둘이 서로 같은 값이라면 결국 최대값이 2개 인것이고 ? 를 출력합니다.
  • 아니라면 대문자 출력을 위해 result에서 -32 를 빼고, 문자열로 형변환해서 출력합니다.

 

오늘 풀었던 문제중에서 10809 번과 1175 번은 특히나!! 제가 제 맘대로 제가 생각하는대로만 풀었던 문제입니다. 모범 답안은 절대 아니라고 생각하니 혹시나 저처럼 공부하시는 분들은 그냥 이런 방법도 있구나 하시고, 다른 분들의 모범답안을 찾아서 공부하시는게 좋다고 생각합니다.

 

저도 제가 풀고나서 다른 분들의 풀이를 찾아보고 하니까요!

 

댓글