이번 문제는 프로그래머스 사이트에 공개된 문제입니다.

문제

https://programmers.co.kr/learn/courses/30/lessons/42839

분석/설계

1. 소수를 몇 개 만들 수 있는지 리턴해야 하므로 소수 구분에 대한 부분은 반드시 포함해야 합니다.

2. numbers에 따른 모든 조합 중 011, 11 같이 중복되는 값이 존재하기 때문에 이런 특정 상태(값)를 제외해야 하며,

   여기서 적절한 알고리즘은 백트래킹 이란 걸 알 수 있습니다.

   백트래킹 알고리즘은 재귀적으로 문제를 하나 풀어나가되 현재 재귀를 통해 확인한 상태가 제한 조건에 해당되지

   않는지 판단하고 해당 상태를 제외하고 다음 단계로 나아가는 방식입니다.

   주의할 점은 시간복잡도에 따른 효율성 제한에 주의해서 사용해야 합니다.

 

구현 코드

import java.util.HashSet;

public class Main {
	    private static HashSet<Integer> set= new HashSet<>(); // 중복 X, 순서 X
	    private static char[] arr;
	    private static boolean[] visited;
	    
	    public int solution(String numbers) {
	        int answer = 0;
	        arr = new char[numbers.length()]; // 문자열 숫자를 쪼개서 저장.
	        visited = new boolean[numbers.length()];
	        
	        for(int i=0; i<numbers.length(); i++){
	            arr[i]=numbers.charAt(i);
	        }
	        dfs("", 0);
	        
	        answer= set.size();
	        return answer;
	    }
	   
	    public void dfs(String str, int idx){  //가능한 숫자 조합을 찾고 소수여부에 따라 set에 추가
	        int num;
	        if(str!="") { // 빈값일경우 실행하지 않음.
	            num= Integer.parseInt(str);
	            if(isPrime(num)) set.add(num); // 소수 여부 판단하여 추가
	        }
	        
	        if(idx== arr.length) return; // 탐색 중지
	        
	        for(int i=0; i<arr.length; i++){ // 
	            if(visited[i]) continue; // 아래 코드를 실행하지 않고 다음 반복을 시작
	            visited[i]= true;
	            dfs(str+arr[i], idx+1); // 다음 조합에 대해 재귀 탐색
	            visited[i]= false;
	        }
	    }
	    
	    public boolean isPrime(int num){ //소수 판별 O(√N)
	        if(num==1||num==0) return false;
	        for(int i=2; i<=Math.sqrt(num); i++){
	            if(num%i==0) return false;
	        }
	        
	        return true;
	    }
}

 

정답 코드(다른 사람의 코드와 비교)

import java.util.HashSet;
class Solution {
    public int solution(String numbers) {
        HashSet<Integer> set = new HashSet<>();
        permutation("", numbers, set);
        int count = 0;
        while(set.iterator().hasNext()){
            int a = set.iterator().next();
            set.remove(a);
            if(a==2) count++;
            if(a%2!=0 && isPrime(a)){
                count++;
            }
        }        
        return count;
    }

    public boolean isPrime(int n){
        if(n==0 || n==1) return false;
        for(int i=3; i<=(int)Math.sqrt(n); i+=2){
            if(n%i==0) return false;
        }
        return true;
    }

        public void permutation(String prefix, String str, HashSet<Integer> set) {
        int n = str.length();
        //if (n == 0) System.out.println(prefix);
        if(!prefix.equals("")) set.add(Integer.valueOf(prefix));
        for (int i = 0; i < n; i++)
          permutation(prefix + str.charAt(i), str.substring(0, i) + str.substring(i+1, n), set);

    }

}

 

비교

permutation을 이용하여 처리하는 방법에 차이가 있습니다.

 

정리

에리토스테네스의 체를 사용하며, 소수 찾기에 효율성이 높고

백트래킹이라는 알고리즘과 백트래킹에 쓰이는 DFS에 대해 다시 한번 살펴봤습니다.

순열조합 풀이 방식은 코드가 아주 간결해 보입니다.

 

참고

https://programmers.co.kr/learn/courses/30/lessons/42839

 

 

 

이번 문제는 프로그래머스 사이트에 공개된 문제입니다.

문제

https://programmers.co.kr/learn/courses/30/lessons/42840

분석/설계

구현을 위해 나눠서 필요한 것들을 생각해봅니다.

 

1. 수포자는 3명의 찍는 방식을 저장할 배열 필요.

2. 정답 배열과 수포자 패턴 배열을 비교하여 점수를 저장할 배열 필요.

3. 패턴 비교 시 문제가 10000문제 이므로 수포자 1의 경우 1,2,3,4,5로 길이 5의 패턴이므로 5 이상의

   길이를 가진 문제일 경우 계속 패턴을 반복하여 비교할 수 있게 인덱스를 적절하게 조정해줘야 함.

   3명의 수포자가 각각 패턴이 다르므로 알맞게 조정 필요. 

3. 점수가 담긴 배열에서 최댓값을 구해야 함.

4. 최댓값을 2번 과정에서 저장한 점수 배열과 비교하여 누가 최대 득점자인지 구하여 리턴.

구현 코드

public static int[] solution(int[] answers) {
	int[] score = {0,0,0}; // 응시자 점수를 담을 배열 생성
	ArrayList<Integer> maxScore = new ArrayList<Integer>(); // 최대 점수 담을 ArrayList  
	int[][] patterns = {{1,2,3,4,5}, 
			    {2,1,2,3,2,4,2,5},
			    {3,3,1,1,2,2,4,4,5,5}}; // 응시자 pattern 배열
	
	for(int i = 0; i < answers.length; i++) {
		
		if (patterns[0][i%5] == answers[i]) { // 1번 응시자
			score[0]++;
		}
		if (patterns[1][i%8] == answers[i]) { // 2번 응시자
			score[1]++;
		}
		if (patterns[2][i%10] == answers[i]) { // 3번 응시자
			score[2]++;
		}
	}
	
	int [] sortScore = score.clone();
	Arrays.sort(sortScore);
	int max = sortScore[2]; // 정렬해서 max값을 얻음
	for(int i = 0; i < score.length; i++) {
		if(score[i]==max) {
			maxScore.add(i+1); // 인덱스 저장
		}
	}
	
	int[] answer = new int[maxScore.size()];
	for(int i = 0; i < answer.length; i++) {
		answer[i] = maxScore.get(i);
	}
return answer;

 

정답 코드(다른 사람의 코드와 비교)

    public int[] solution(int[] answer) {
        int[] a = {1, 2, 3, 4, 5};
        int[] b = {2, 1, 2, 3, 2, 4, 2, 5};
        int[] c = {3, 3, 1, 1, 2, 2, 4, 4, 5, 5};
        int[] score = new int[3];
        for(int i=0; i<answer.length; i++) {
            if(answer[i] == a[i%a.length]) {score[0]++;}
            if(answer[i] == b[i%b.length]) {score[1]++;}
            if(answer[i] == c[i%c.length]) {score[2]++;}
        }
        int maxScore = Math.max(score[0], Math.max(score[1], score[2]));
        ArrayList<Integer> list = new ArrayList<>();
        if(maxScore == score[0]) {list.add(1);}
        if(maxScore == score[1]) {list.add(2);}
        if(maxScore == score[2]) {list.add(3);}
        return list.stream().mapToInt(i->i.intValue()).toArray();
    }

 

비교

1. 정답과 수포자 패턴 비교

   정답과 패턴 비교 시 패턴 길이를 하드코딩을 했는데 문제의 수포자가 3명으로 제한되어있지만

   N명일 경우 패턴 배열의 length 메서드를 써서 풀어야 합니다.

 

2. 최대 값 구하기

   최대 값을 구하기 위해 Math 클래스의 max를 사용했고 저같은 경우 Arrays 클래스의 sort 메서드를

   사용하여 최댓값을 구했습니다.

   참고로 Arrays.sort 메서드의 경우 내부 정렬을 위해 기본 자료형은 Quick sort 그리고 객체는 Tim sort

   를 사용합니다.

   Tim Sort는 참고에 Naver D2 글을 참고하면 도움이 될 것 같습니다.

 

3. 결괏값 배열 리턴

   제 코드에서는 ArrayList의 꺼내어 정답 배열(최대 득점 자) 옮기기 위해 for 문을 사용했는데요.

   다른 사람 코드는 stream을 mapToInt 메서드를 활용하여 Int Stream으로 전환 후 toArray 메서드로

   배열로 리턴할 수 있게 처리하였습니다.

정리

Stream을 JDK 8부터 소개되었는데 배열, 컬렉션을 다루고 여기서 특정 값을 탐색할 때 아주 유용합니다.

람다를 이용해서 코드량을 많이 줄 일수 있습니다.

익숙한 for 문을 사용한 접근 대신 stream으로 접근도 많이 시도해봐야 해봐야 할 것 같습니다.

stream 관련해서는 잘 정리된 블로그 글이 있어 참고에 링크했습니다.

참고

https://programmers.co.kr/learn/courses/30/lessons/42840

https://d2.naver.com/helloworld/0315536

https://futurecreator.github.io/2018/08/26/java-8-streams/

알고리즘 문제를 구현한 코드와 정답을 비교해보도록 하겠습니다.

 

문제

알파벳 대문자와 숫자(0~9)로만 구성된 문자열이 입력으로 주어집니다.

이때 모든 알파벳을 오름차순으로 정렬하여 이어서 출력한 뒤에,

그 뒤에 모든 숫자를 더한 값을 이어서 출력합니다.

 

입력 조건
문자열 S (1 <= S의 길이 <= 10000)

입력 예시1
K1KA5CB7

출력 예시1
ABCKK13

입력 예시2
AJKDLSI412K4JS9D

출력 예시2
ADDIJJJKKLSS20

 

구현을 위해 나눠서 필요한 것들을 생각해봅니다.

 

1. 문자열을 입력받기 위한 코드가 필요.

2. 입력받은 문자열은 알파벳 대문자와 숫자로 구성되는 제약이 있는데 알파벳을 오름차순으로 정렬 필요.

   따라서 알파벳과 숫자 분리 필요.

3. 분리한 숫자 문자열은 숫자로 변환하여 합을 구함.

4. 정렬된 알파벳 문자열과 숫자를 합친 문자열을 만들어 출력.

 

구현 코드

import java.util.Arrays;
import java.util.Scanner;

public class Main  {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		String S = scanner.next(); // 시스템 입력을 받아 문자열 저장 
		char tempArr[] = S.toCharArray(); // 문자열을 Char 형의 Array로 변환
		int num = 0; 
		String str = "";
		
		Arrays.sort(tempArr); // 오름 차순 정렬
		
		for(int i=0; i<tempArr.length; i++) {
			// 대문자 A~Z 일 경우에 문자열을 str 변수에 추가.
			if(tempArr[i] -'A' >= 0 && tempArr[i]-'Z' <= 0) {
				str += tempArr[i];
			} else { // 아닐 경우 숫자를 따로 더한다.
				num += tempArr[i] - '0';
			}
		}
		
		if(num != 0) { // 알파벳 단독 구성인 경우를 구분하여 출력
			System.out.println(str + num);
		} else {
			System.out.println(str);
		}
		scanner.close();
	}
}

 

정답 코드

import java.util.*;

public class Main {

    public static String str;
    public static ArrayList<Character> result = new ArrayList<Character>();
    public static int value = 0;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        str = sc.next();

        // 문자를 하나씩 확인하며
        for (int i = 0; i < str.length(); i++) {
            // 알파벳인 경우 결과 리스트에 삽입
            if (Character.isLetter(str.charAt(i))) {
                result.add(str.charAt(i));
            }
            // 숫자는 따로 더하기
            else {
                value += str.charAt(i) - '0';
            }
        }

        // 알파벳을 오름차순으로 정렬
        Collections.sort(result);

        // 알파벳을 차례대로 출력
        for (int i = 0; i < result.size(); i++) {
            System.out.print(result.get(i));
        }

        // 숫자가 하나라도 존재하는 경우 가장 뒤에 출력
        if (value != 0) System.out.print(value);
        System.out.println();
    }
}

 

 

비교

1. 정렬
제가 작성한 코드의 경우 문자열 입력 후 Char 형 Array 전환 후 바로 정렬했다는 점이고
정답의 경우 알파벳 문자를 분리하여 정렬하였습니다. 
따라서 여기서 정렬할 문자 길이에 따른 시간 차이가 발생합니다.

2. 알파벳 문자인지 확인하기 위한 코드
알파벳 문자인지 구분을 위해 Character isLetter 메소드를 사용했다는 점입니다.

3. for문 사용
제가 작성한 코드의 경우 알파벳 문자와 숫자 문자를 구분 및 변수에 저장하기 위해 1번 사용.
정답의 경우 알파벳 출력을 위해 for문을 한번 더 사용하여 총 2번을 사용합니다.

 

정리

정답에서 char문자 구분을 위해 사용한 메소드 외에 알파벳 문자만을 위한 메서드와 숫자를 구분하는 메서드가
있어서 정리해봅니다.

1. Character isLetter 메서드
    char 값이 문자인지 판단하여 true / false를 리턴.

2.  Character isDigit 메서드
    char 값이 숫자인지 판단하여 true / false를 리턴.

3.  Character isAlphabetic 메서드
    char 값이 알파벳 문자인지 판단하여 true / false를 리턴

 

참고

https://www.baeldung.com/java-character-isletter-isalphabetic
https://github.com/ndb796/python-for-coding-test/blob/master/12/2.java

 

 

 

'Algorithm > 구현' 카테고리의 다른 글

[백준] 참외밭  (0) 2022.12.23
[백준] 배열돌리기 1  (0) 2022.12.23
[백준] 마인크래프트  (0) 2022.12.23
괄호의 값  (1) 2022.12.14

+ Recent posts