본문 바로가기

항해 99/Java

페어 프로그래밍 - 코딩 테스트 3

개인 풀이

코드 리뷰 - ChatGPT

 

29. 최대 공약수와 최소 공배수

class Solution {
    public int[] solution(int n, int m) {
        // 유클리드 호제법 사용
        int[] answer = {gcd(n, m), lcm(n, m)};
        return answer;
    }

    //최대 공약수 구하기
    public static int gcd(int a, int b) {
        if (b == 0) return a;
        return gcd(b, a % b);
    }

    //최소 공배수 구하기
    public static int lcm(int a, int b) {
        return a * b / gcd(a, b);
    }
}
  • 유클리드 호제법 사용이 핵심

코드 리뷰

  • 가독성
    • 명명 규칙: 함수명 gcd와 lcm은 각각 최대 공약수와 최소 공배수를 의미하는 일반적인 약어로 적절하게 사용되었습니다. 변수명 n, m, a, b도 수학적 표현과 일치하여 이해하기 쉽습니다.
    • 간결성: 코드가 간결하고 직관적입니다. 유클리드 호제법의 구현이 잘 되어 있으며, solution 메서드 내에서 결과 배열을 직접 반환하는 방식은 간결하고 명확한 로직을 보여줍니다.
  • 성능
    • 유클리드 호제법: 최대 공약수를 구하는 데 가장 효율적인 알고리즘 중 하나를 사용하였습니다. 이는 성능 측면에서 매우 우수합니다.
    • 최소 공배수 계산: 최소 공배수를 구하기 위해 최대 공약수를 활용하는 방식은 연산량을 최소화하며 효율적입니다.
  • 확장성
    • 코드의 현재 구조는 두 수에 대한 최대 공약수와 최소 공배수를 계산하기 위해 설계되었습니다. 확장성 측면에서, 여러 수에 대한 최대 공약수나 최소 공배수를 구하는 기능으로 확장할 때는 추가적인 로직 변경이 필요할 수 있습니다.
    • gcd와 lcm 메서드가 static으로 선언되어 있어, 다른 클래스에서도 이 메서드들을 직접 사용할 수 있습니다. 이는 유틸리티 메서드로서의 재사용성과 확장성을 높여줍니다.
  • 유지보수
    • 단순성과 명확성: 코드의 단순성과 명확성은 유지보수를 용이하게 합니다. 추후 로직 변경이나 기능 추가 시에도 기존 구조를 쉽게 이해하고 수정할 수 있습니다.
    • 재사용 가능성: gcd와 lcm 메서드는 다른 문제 해결에도 재사용할 수 있어, 코드의 재사용성과 유지보수성을 향상시킵니다.
  • 개선안
    • 예외 처리: 현재 코드는 입력 값에 대한 예외 처리가 없습니다. 예를 들어, n이나 m이 음수인 경우에 대한 처리를 추가할 수 있습니다. 실제 사용 환경에서는 입력 값 검증이 중요할 수 있으므로, 이를 고려하여 코드를 보완하는 것이 좋습니다.
    • 코드 주석 추가: 코드의 가독성을 더욱 향상시키기 위해 주요 로직에 대한 간단한 주석을 추가하는 것을 고려할 수 있습니다. 예를 들어, gcd와 lcm 메서드의 작동 원리에 대한 설명을 추가하는 것입니다. 이미 코드가 간결하고 명확하기 때문에 이 부분은 선택적으로 고려할 수 있습니다.

개선 코드

class Solution {
    /**
     * 두 수의 최대 공약수(GCD)와 최소 공배수(LCM)를 계산합니다.
     * 
     * @param n 첫 번째 수
     * @param m 두 번째 수
     * @return 최대 공약수와 최소 공배수를 담은 배열
     */
    public int[] solution(int n, int m) {
        // 입력 값 검증
        if (n <= 0 || m <= 0) {
            throw new IllegalArgumentException("n과 m은 양의 정수여야 합니다.");
        }
        
        // 유클리드 호제법을 사용하여 최대 공약수와 최소 공배수를 계산
        int gcd = gcd(n, m); // 최대 공약수 계산
        int lcm = lcm(n, m, gcd); // 최소 공배수 계산, gcd를 재사용하여 효율적 계산
        return new int[]{gcd, lcm};
    }

    /**
     * 최대 공약수(GCD)를 유클리드 호제법으로 계산합니다.
     * 
     * @param a 첫 번째 수
     * @param b 두 번째 수
     * @return 두 수의 최대 공약수
     */
    public static int gcd(int a, int b) {
        if (b == 0) return a;
        return gcd(b, a % b);
    }

    /**
     * 최소 공배수(LCM)를 계산합니다. LCM = (a * b) / GCD
     * 
     * @param a 첫 번째 수
     * @param b 두 번째 수
     * @param gcd 두 수의 최대 공약수
     * @return 두 수의 최소 공배수
     */
    public static int lcm(int a, int b, int gcd) {
        return a * b / gcd;
    }
}

 

유클리드 호제법 코드

//유클리드 호제법
    //재귀 방식
    public static int gcd(int a, int b) {
        if (b == 0) return a;
        return gcd(b, a % b);
    }

    //반복문 방식
    public static int gcdLoop(int a, int b) {
        while (b != 0) {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }

    //최소 공배수 구현
    public static int lcm(int a, int b) {
        return a * b / gcd(a, b);
    }

    //N수의 최대 공약수 구하기
    public static int gcd(int[] arr) {
        int result = arr[0];
        for (int i = 1; i < arr.length; i++) {
            result = gcd(result, arr[i]);
        }
        return result;
    }

    //N수의 최소 공배수 구하기
    public static int lcm(int[] arr) {
        int result = arr[0];
        for (int i = 1; i < arr.length; i++) {
            result = lcm(result, arr[i]);
        }
        return result;
    }

 

 

 

30. 3진수 뒤집기

class Solution {
    public int solution(int n) {
    	//2, 8, 16 진수는 Binary, Octal, Hex 함수 사용
        //toString(int n, int radix)로 사용 가능
        // 3진수 변환 및 앞뒤로 뒤집기 -> radix 위치에는 변환할 진수를 입력
        String third = Integer.toString(n, 3);
        String reverseThird = new StringBuilder(third).reverse().toString();

        //n 진수 -> 10진수는 Integer.parseInt(String s, int radix)로 변환
        // 10진수 변환 -> radix 위치에는 n이 몇 진수인지 입력
        return Integer.parseInt(reverseThird, 3);
    }
}

 

코드 리뷰

  • 가독성
    • 명명 규칙: 변수명 third와 reverseThird는 각각 3진수 변환 결과와 그 결과의 뒤집힌 상태를 명확하게 설명하고 있어, 이해하기 쉽습니다.
    • 간결성: 코드는 매우 간결하고 직관적입니다. Integer.toString과 StringBuilder.reverse 메서드를 통해 목적을 달성하는 과정이 명확하게 표현되어 있습니다.
  • 성능
    • 효율적 변환 과정: 주어진 정수를 3진수로 변환하고 이를 뒤집는 과정은 Java의 기본 라이브러리를 통해 효율적으로 이루어집니다. 여기서 성능상의 큰 이슈는 없어 보입니다.
  • 확장성
    • 확장 가능성: 현재 구현은 3진수 변환에 특화되어 있지만, 메서드의 구조를 유지하면서 다른 진수 변환에도 쉽게 적용할 수 있습니다. 예를 들어, radix 값을 파라미터로 받아 다양한 진수 변환을 지원하도록 확장할 수 있습니다.
  • 유지보수
    • 유지보수성: 이 코드는 매우 간결하고, 사용된 메서드들은 Java 표준 라이브러리의 일부로서 잘 문서화되어 있습니다. 따라서 유지보수 측면에서도 뛰어난 편입니다.
    • 에러 처리: 이 코드는 입력 값에 대한 명시적인 에러 처리나 검증을 포함하고 있지 않습니다. n이 음수인 경우에도 정상적으로 동작하지만, 특별한 예외 처리가 필요한 상황에 대비하여 입력 값 검증 로직을 추가할 수 있습니다.
  • 개선안
    • 입력 값 검증: 입력 값 n에 대한 검증 로직을 추가하여, 예상치 못한 입력 값에 대해 더 견고한 코드를 작성할 수 있습니다.
    • 확장성 고려: 다른 진수 변환을 지원하도록 메서드를 일반화하는 것을 고려할 수 있습니다. 예를 들어, 변환할 진수를 메서드의 파라미터로 받아 처리할 수 있게 하는 것입니다.

개선 코드

class Solution {
    // 진수 변환 및 뒤집기 메서드를 일반화
    public int solution(int n, int radix) {
        if (n < 0) {
            throw new IllegalArgumentException("n은 양의 정수여야 합니다.");
        }
        
        String converted = Integer.toString(n, radix);
        String reversed = new StringBuilder(converted).reverse().toString();
        
        return Integer.parseInt(reversed, radix);
    }
}

 

 

 

31. 이상한 문자 만들기

class Solution {
    public String solution(String s) {
        String answer = "";
        int cnt = 0;
        String[] str = s.split("");

        for (String word : str) {
            cnt = (word.contains(" ")) ? 0 : cnt + 1;
            answer += (cnt % 2 == 0) ? word.toLowerCase() : word.toUpperCase();
        }
        return answer;
    }
}
  • 제한 사항을 이해하는 것이 중요함
    • 각 단어 별로 0번째 인덱스를 짝수번째 인덱스로 처리(문자 자체의 순서로 짝홀수 판단이 아니라 인덱스 번호 기준으로 짝홀수 판단)
    • 코드에서는 문자열에 대한 변환은 cnt 변수의 홀수/짝수 여부에 따라 결정 됨, cnt는 공백을 만날 때마다 0으로 초기화 됨(공백이 아닐 때 1씩 증가), 그렇기 때문에 단어의 첫 단어에서 cnt는 홀수이기 때문에 단어 변환 시 false 값에 UpperCase를 실행해야 올바른 결과가 나옴

코드 리뷰

  • 가독성
    • 명명 규칙: 변수명 cnt와 str은 각각 현재 처리 중인 문자의 인덱스를 카운트하고, 입력 문자열을 한 문자씩 분리한 배열을 저장하는 용도로 사용되고 있습니다. cnt는 index나 charIndex 같은 보다 명확한 이름으로 변경하는 것이 좋을 수 있습니다.
    • 코드 구조: 코드는 직관적이며, 로직이 명확합니다. 하지만 String 연산을 반복문 안에서 +=로 수행하는 것은 성능에 좋지 않은 영향을 줄 수 있습니다.
  • 성능
    • 문자열 연산: Java에서 문자열(String)은 변경 불가능(immutable)하므로, += 연산자를 사용할 때마다 새로운 String 객체가 생성됩니다. 대량의 데이터를 처리할 경우, 이 방식은 메모리 사용량이 크게 증가하고 성능 저하를 일으킬 수 있습니다. StringBuilder를 사용하는 것이 더 효율적입니다.
  • 확장성
    • 문자열 연산: Java에서 문자열(String)은 변경 불가능(immutable)하므로, += 연산자를 사용할 때마다 새로운 String 객체가 생성됩니다. 대량의 데이터를 처리할 경우, 이 방식은 메모리 사용량이 크게 증가하고 성능 저하를 일으킬 수 있습니다. StringBuilder를 사용하는 것이 더 효율적입니다.
  • 유지보수
    • 유지보수 용이성: StringBuilder를 사용하도록 변경하면, 미래에 코드를 수정할 때 성능 문제를 덜 걱정하면서 유지보수를 할 수 있습니다.
    • 매직 넘버 사용 최소화: 코드 내에서 직접적으로 사용된 숫자(예: 0, 2)는 매직 넘버로 간주될 수 있으며, 이를 상수로 선언하여 사용하는 것이 유지보수에 도움이 됩니다.

개선코드

class Solution {
    public String solution(String s) {
        StringBuilder answer = new StringBuilder();
        int charIndex = 0; // 명확한 변수명 사용

        for (char c : s.toCharArray()) { // 문자열을 한 번에 배열로 변환
            if (c == ' ') {
                charIndex = 0; // 공백 발견 시 인덱스 초기화
                answer.append(c);
            } else {
                answer.append((charIndex % 2 == 0) ? Character.toUpperCase(c) : Character.toLowerCase(c));
                charIndex++;
            }
        }
        return answer.toString();
    }
}

변경사항 요약

  • StringBuilder를 사용하여 문자열 합치기 성능 개선.
  • charIndex라는 변수명으로 cnt를 변경하여 목적을 더 명확하게 표현.
  • String.split("") 대신 String.toCharArray()를 사용하여 문자 배열 생성 과정을 간소화. 이는 성능을 약간 개선할 수 있으며, 코드의 명확성을 높여줍니다.

 

 

 

32. 삼총사

class Solution {
    public int solution(int[] number) {
        int answer = 0;
        for(int i = 0; i < number.length; i++) {
            for(int j = i + 1; j < number.length; j++) {
                for(int k = j + 1; k < number.length; k++) {
                    if(number[i] + number[j] + number[k] == 0) {
                        answer++;
                    }
                }
            }
        }
        return answer;
    }
}

 

코드 리뷰

  • 가독성
    • 명확한 로직: 코드는 세 개의 중첩된 for 반복문을 사용하여 배열의 모든 가능한 세 수 조합을 탐색합니다. 이 방법은 직관적이며, 문제의 요구사항을 명확하게 반영합니다.
    • 변수명: answer, i, j, k 등의 변수명은 이 코드의 목적에 부합하며, 해당 문제에서 널리 사용되는 관례를 따릅니다.
  • 성능
    • 시간 복잡도: 이 알고리즘의 시간 복잡도는 O(n^3)입니다. 여기서 n은 배열 number의 길이입니다. 큰 입력 값에 대해 성능 저하가 발생할 수 있습니다.
    • 성능 최적화: 복잡도를 낮출 수 있는 알고리즘(예: 정렬 후 투 포인터 알고리즘 사용)을 적용하여 성능을 개선할 수 있습니다. 그러나 이 문제의 경우, 간단한 입력 값에 대해서는 현재의 구현도 충분히 효율적일 수 있습니다.
  • 확장성
    • 코드는 현재 주어진 문제에 특화되어 있습니다. 확장성을 고려하여 다양한 조합의 합을 찾는 일반화된 함수로 리팩토링할 수 있지만, 이는 문제의 범위를 넘어설 수 있습니다.
  • 유지보수
    • 유지보수성: 코드는 간결하고 이해하기 쉬워, 유지보수 측면에서 좋습니다. 문제의 요구사항이 변경되지 않는 한, 복잡한 수정 없이도 잘 작동할 것입니다.
    • 에러 처리: 이 코드는 입력 배열이 null이거나 길이가 3 미만인 경우를 처리하지 않습니다. 이러한 상황에 대한 검증 로직을 추가하는 것이 좋을 수 있습니다.

 

 

 

33. 크기가 작은 부분 문자열

class Solution {
    public int solution(String t, String p) {
        int cnt = 0;
        int strLength = t.length() - p.length();
        for (int i = 0; i <= strLength; i++) {
            String part = t.substring(i, i + p.length());
            cnt += (Long.parseLong(part) <= Long.parseLong(p)) ? 1 : 0;
        }
        return cnt;
    }
}
  • 변수 타입의 범위 초과에 주의

코드 리뷰

  • 가독성
    • 명명 규칙: 변수명 cnt와 strLength는 각각 조건을 만족하는 부분 문자열의 수와 탐색해야 할 최대 길이를 나타냅니다. 이는 코드의 목적을 이해하는 데 도움을 줍니다.
    • 코드 구조: 코드는 간결하며, 로직이 명확합니다. substring 메서드를 통해 부분 문자열을 추출하고, 이를 Long.parseLong을 사용해 숫자로 변환한 뒤 비교하는 과정은 직관적입니다.
  • 성능
    • 문자열에서 숫자로의 변환: Long.parseLong 메서드를 사용하는 방식은 문자열 내용이 숫자로만 구성되어 있을 경우에 유효합니다. 이 과정에서 발생하는 변환 비용은 고려할 필요가 있습니다. 특히 큰 문자열을 처리할 때 성능 저하의 원인이 될 수 있습니다.
    • 부분 문자열 생성: substring 메서드를 사용하여 반복적으로 부분 문자열을 생성하는 것은 메모리 사용량을 증가시킬 수 있습니다.
  • 확장성
    • 코드는 현재 주어진 문제를 해결하기 위한 특정한 경우에 초점을 맞추고 있으며, 다른 유형의 비교나 다른 조건을 추가하기 위한 확장성은 제한적입니다.
  • 유지보수
    • 예외 처리: 문자열 t 또는 p가 숫자가 아닌 문자를 포함하고 있거나, 매우 큰 숫자를 포함하는 경우 Long.parseLong에서 예외가 발생할 수 있습니다. 이에 대한 예외 처리 로직이 포함되어 있지 않습니다.
  • 개선안
    • 예외 처리 추가: 입력 문자열이 숫자로만 구성되어 있는지, 그리고 숫자의 크기가 Long 타입으로 변환 가능한 범위 내에 있는지 검증하는 로직을 추가할 수 있습니다.
    • 성능 최적화: 가능하다면, 숫자 변환 과정을 최소화하거나 다른 방법을 모색하여 성능을 개선할 수 있습니다. 예를 들어, 문자열을 직접 비교하는 방식으로 로직을 변경할 수 있습니다. 그러나 이 경우에는 문자열 t와 p가 숫자로만 구성되어 있고, 동일한 자릿수를 갖는다는 가정 하에 가능합니다.

개선코드

class Solution {
    public int solution(String t, String p) {
        int cnt = 0;
        int strLength = t.length() - p.length();
        for (int i = 0; i <= strLength; i++) {
            // 예외 처리 로직 추가 (예시)
            // 숫자 변환 대신 문자열 비교로 성능 개선
            String part = t.substring(i, i + p.length());
            if (part.compareTo(p) <= 0) {
                cnt++;
            }
        }
        return cnt;
    }
}

 

스트림 사용 답안

import java.util.stream.LongStream;

class Solution {
    public int solution(String t, String p) {
        long targetNumber = Long.parseLong(p);
        int targetNumberLength = p.length();

        return (int) LongStream.range(0L, t.length() - targetNumberLength + 1L)
                .mapToObj(i -> t.substring((int) i, (int) i + targetNumberLength))
                .mapToLong(Long::parseLong)
                .filter(number -> number <= targetNumber)
                .count();
    }
}

 

 

 

34. 최소 직사각형

import java.util.Arrays;

class Solution {
    public int solution(int[][] sizes) {
        for (int i = 0; i < sizes.length; i++) {
            Arrays.sort(sizes[i]);
        }
        int max1 = Arrays.stream(sizes).mapToInt(arr -> arr[0]).max().orElse(0);
        int max2 = Arrays.stream(sizes).mapToInt(arr -> arr[1]).max().orElse(0);
        return max1 * max2;
    }
}

 

코드 리뷰

  • 가독성
    • 명확한 로직: 각 명함 배열의 요소를 정렬하여 가로 길이가 항상 세로 길이보다 작거나 같도록 조정합니다. 이후 모든 명함의 가로와 세로 길이 중 최대값을 찾아 이 둘의 곱으로 최소 직사각형의 면적을 계산합니다. 이 접근 방식은 직관적이며 문제를 효과적으로 해결합니다.
    • 코드 구조: Arrays.sort와 스트림 API를 사용하여 코드가 간결하고 읽기 쉽습니다.
  • 성능
    • 효율적인 처리: 각 명함을 개별적으로 정렬한 뒤 최대 가로 길이와 최대 세로 길이를 찾는 방식은 주어진 문제에 대해 효율적입니다. 각 명함을 정렬하는 데 걸리는 시간 복잡도는 O(n log n)이지만, 각 명함의 크기가 2로 고정되어 있기 때문에 실제로는 거의 상수 시간에 가깝게 작동합니다.
    • 스트림 API 사용: 스트림 API를 사용하는 것은 가독성을 높이지만, 때로는 for 루프에 비해 성능이 약간 떨어질 수 있습니다. 그러나 이 경우에는 데이터 양이 크지 않다면 성능 차이가 크게 문제되지 않을 수 있습니다.
  • 확장성
    • 코드는 특정 문제, 즉 모든 명함을 수용할 수 있는 최소 직사각형의 면적을 찾는 문제에 맞춰져 있습니다. 확장성 측면에서는, 이 로직을 다른 유형의 최소 영역 문제에 적용하려면 조정이 필요할 수 있습니다.
  • 유지보수
    • 유지보수 용이성: 코드는 간결하고, 사용된 메서드들은 Java 표준 라이브러리의 일부로 잘 문서화되어 있어 유지보수 측면에서 좋습니다.
    • 에러 처리와 입력 검증: 코드에는 입력 배열 sizes가 null이거나 빈 배열인 경우를 처리하는 로직이 포함되어 있지 않습니다. 실제 응용 프로그램에서는 이러한 경우에 대한 처리를 추가하는 것이 좋을 수 있습니다.

개선 코드

class Solution {
    public int solution(int[][] sizes) {
        if (sizes == null || sizes.length == 0) {
            throw new IllegalArgumentException("sizes 배열이 유효하지 않습니다.");
        }

        for (int[] size : sizes) {
            Arrays.sort(size);
        }
        int max1 = Arrays.stream(sizes).mapToInt(arr -> arr[0]).max().orElseThrow(() -> new IllegalStateException("최대 가로 길이 계산 실패"));

 

다른 답안 코드

class Solution {
    public int solution(int[][] sizes) {
        int length = 0, height = 0;
        for (int[] card : sizes) {
            length = Math.max(length, Math.max(card[0], card[1]));
            height = Math.max(height, Math.min(card[0], card[1]));
        }
        int answer = length * height;
        return answer;
    }
}

 

 

 

35. 시저 암호

class Solution {
    public String solution(String s, int n) {
        String answer = "";
        
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (Character.isLowerCase(ch)) {
                ch = (char) ((ch - 'a' + n) % 26 + 'a');
            } else if (Character.isUpperCase(ch)) {
                ch = (char) ((ch - 'A' + n) % 26 + 'A');
            }
            answer += ch;
        }
        
        return answer;
    }
}

 

코드 리뷰

  • 가독성
    • 명확한 로직: 알파벳 문자에 대해 n만큼 이동시키고, 이동한 결과가 알파벳 범위를 벗어나는 경우 순환시키는 로직을 구현했습니다. 이는 시저 암호의 기본 원리를 잘 반영하고 있습니다.
    • 변수명: ch와 같은 변수명은 사용되는 문맥에서 충분히 이해가 가능하며, answer 변수명도 반환되는 결과값을 잘 나타냅니다.
  • 성능
    • 문자열 연산: Java에서 문자열(String)은 변경 불가능(immutable)하기 때문에, += 연산자를 사용할 때마다 새로운 String 객체가 생성됩니다. 큰 입력값에 대해서는 이 방식이 메모리 사용량을 증가시키고, 성능 저하를 일으킬 수 있습니다. StringBuilder를 사용하는 것이 더 효율적입니다.
  • 확장성
    • 코드는 현재 주어진 문제, 즉 문자열을 시저 암호로 변환하는 특정한 경우에 초점을 맞추고 있습니다. 확장성 측면에서는, 이 로직을 다양한 암호화 방식에 적용하려면 추가적인 수정이 필요할 수 있습니다.
  • 유지보수
    • 유지보수 용이성: 코드는 간결하고, 로직이 명확하기 때문에 유지보수가 비교적 용이합니다.
    • 에러 처리와 입력 검증: 이 코드는 입력 문자열 s가 null인 경우나 n의 값이 음수인 경우를 명시적으로 처리하지 않습니다. 이러한 입력에 대한 처리를 추가하여 코드의 견고함을 높일 수 있습니다.

개선 코드

class Solution {
    public String solution(String s, int n) {
        StringBuilder answer = new StringBuilder();
        
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (Character.isLowerCase(ch)) {
                ch = (char) ((ch - 'a' + n) % 26 + 'a');
            } else if (Character.isUpperCase(ch)) {
                ch = (char) ((ch - 'A' + n) % 26 + 'A');
            }
            answer.append(ch);
        }
        
        return answer.toString();
    }
}

변경사항 요약

  • StringBuilder를 사용하여 문자열 합치기의 성능을 개선했습니다. 이 방식은 메모리 할당과 가비지 컬렉션의 부하를 줄여줍니다.
  • 입력 문자열 s가 null이거나 n의 값에 대한 예외 처리는 구현에 따라 추가할 수 있습니다. 예외 처리를 추가하는 것은 실제 애플리케이션의 요구 사항과 사용 환경에 따라 달라질 수 있습니다.

 

 

36. 숫자 문자열과 영단어

class Solution {
    public int solution(String s) {
        for (Number num : Number.values()) {
            s = s.replaceAll(num.name(), String.valueOf(num.ordinal()));
        }

        return Integer.parseInt(s); 
    }
    enum Number {
        zero, one, two, three, four, five, six, seven, eight, nine
    }
}

 

 

코드 리뷰

  • 가독성
    • 명확한 로직: enum Number를 사용하여 숫자에 해당하는 영단어를 관리하는 방법은 코드의 가독성을 높입니다. 각 숫자와 그에 대응하는 영단어가 명확하게 매핑되어 있어, 코드의 목적을 이해하기 쉽습니다.
    • 코드 구조: replaceAll 메서드를 사용하여 각 영단어를 숫자로 치환하는 방식은 간결하며, 문제 해결 방법을 직관적으로 표현합니다.
  • 성능
    • 성능 고려: replaceAll 메서드는 내부적으로 정규 표현식을 사용하며, 이는 문자열 처리에서 상대적으로 느릴 수 있습니다. 그러나 주어진 s의 길이가 최대 50으로 제한되어 있기 때문에, 이 경우에는 큰 성능 문제가 되지 않을 수 있습니다. 그럼에도 불구하고, 큰 데이터 세트를 처리해야 하는 상황에서는 성능이 문제가 될 수 있습니다.
  • 확장성
    • 확장 가능성: 이 코드는 숫자를 영단어로 치환하는 특정 문제에 초점을 맞추고 있습니다. enum을 활용하는 방식은 다른 유형의 문자열 치환 문제에도 적용될 수 있는 좋은 예시입니다. 확장성 측면에서, 추가적인 영단어나 다른 치환 규칙이 필요한 경우 enum에 항목을 추가하거나 수정하기만 하면 됩니다.
  • 유지보수
    • 유지보수 용이성: enum을 사용하는 방식은 유지보수 측면에서 유리합니다. 치환해야 하는 영단어나 숫자가 변경되거나 추가되는 경우, Number enum만 수정하면 되기 때문에 코드의 나머지 부분은 변경할 필요가 없습니다.
    • 에러 처리와 입력 검증: 주어진 제한 사항에 따르면, s의 길이와 값 범위에 대한 검증이 필요하지 않을 수 있습니다. 그러나 일반적으로, 입력 데이터의 유효성을 검증하는 로직을 추가하는 것이 좋은 프로그래밍 관행입니다.

개선 코드

class Solution {
    public int solution(String s) {
        StringBuilder answer = new StringBuilder();
        StringBuilder wordBuffer = new StringBuilder();
        String[] numberWords = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
        
        for (char ch : s.toCharArray()) {
            if (Character.isDigit(ch)) {
                answer.append(ch); // 숫자인 경우 바로 추가
            } else {
                wordBuffer.append(ch); // 문자인 경우 버퍼에 추가
                for (int i = 0; i < numberWords.length; i++) {
                    if (wordBuffer.toString().equals(numberWords[i])) {
                        answer.append(i); // 버퍼의 내용이 숫자 단어와 일치하는 경우
                        wordBuffer.setLength(0); // 버퍼 초기화
                        break;
                    }
                }
            }
        }
        
        return Integer.parseInt(answer.toString());
    }
}
  • 문자열 s를 한 번만 순회하며 처리합니다.
  • 각 문자가 숫자인 경우 바로 answer에 추가합니다.
  • 문자가 알파벳인 경우, wordBuffer에 추가하여 영단어를 구성합니다.
  • wordBuffer의 내용이 숫자 영단어 중 하나와 일치하면 해당 숫자를 answer에 추가하고 wordBuffer를 초기화합니다.
  • 이 방식은 replaceAll 메서드를 사용할 때 발생할 수 있는 여러 번의 문자열 순회와 정규 표현식 처리를 피함으로써 성능을 개선합니다.
  • 코드에서 영어 숫자를 숫자로 바꾸는데 기존 s의 순서를 지킬 수 있는 로직은 없음.

다른 답안 코드

class Solution {
    public int solution(String s) {
        String[] strArr = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
        for(int i = 0; i < strArr.length; i++) {
            s = s.replaceAll(strArr[i], Integer.toString(i));
        }
        return Integer.parseInt(s);
    }
}

 

 

 

37. 문자열 내 마음대로 정렬하기

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


class Solution {
    public String[] solution(String[] strings, int n) {
        Arrays.sort(strings);
        String[] answer = new String[strings.length];

        for(int i = 0; i < strings.length; i++) {
            answer[i] = strings[i].substring(n, n+1);
        }
        Arrays.sort(answer);

        List<String> list = new ArrayList<>(Arrays.asList(strings));
        for(int i = 0; i < answer.length; i++) {
            for(int j = 0; j < list.size(); j++) {
                if(list.get(j).substring(n, n+1).equals(answer[i])) {
                    answer[i] = list.get(j);
                    list.remove(list.get(j));
                    break;
                }
            }
        }
        return answer;
    }
}

 

코드 리뷰

  • 로직의 오류: 초기에 strings 배열을 사전순으로 정렬하는 것은 올바른 접근입니다. 그러나 이후에 n번째 문자만을 추출하여 이를 다시 정렬하는 과정은 문제의 요구사항을 정확히 충족시키지 못합니다. n번째 문자로 strings 배열을 정렬한 뒤, n번째 문자가 동일한 경우에만 사전순으로 정렬해야 하지만, 현재 로직은 n번째 문자만 고려하여 전체 문자열을 재정렬하지 않습니다.
  • 비효율적인 접근: answer 배열에 n번째 문자를 기준으로 정렬된 결과를 저장하려고 하나, 이후 과정에서 다시 전체 문자열을 검색하여 원래 문자열을 찾는 방식은 비효율적입니다. 이 과정은 불필요한 중복 검색을 수행하며, 특히 list.remove() 호출은 리스트의 크기가 클 때 성능 저하를 일으킬 수 있습니다.
  • 개선안
    • strings 배열을 한 번만 정렬하되, 정렬 기준을 두 가지로 설정합니다: 우선 n번째 문자를 기준으로 하고, 그것이 같은 경우 전체 문자열의 사전순을 두 번째 기준으로 합니다.
    • Comparator를 사용하여 사용자 정의 정렬 기준을 설정합니다.

개선 코드

import java.util.Arrays;
import java.util.Comparator;

class Solution {
    public String[] solution(String[] strings, int n) {
        Arrays.sort(strings, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                // 먼저 n번째 문자를 기준으로 비교
                if (s1.charAt(n) != s2.charAt(n)) {
                    return s1.charAt(n) - s2.charAt(n);
                } else {
                    // n번째 문자가 같다면 전체 문자열 사전순으로 비교
                    return s1.compareTo(s2);
                }
            }
        });
        return strings;
    }
}

//전체 코드 구조
//Arrays.sort 메서드를 사용하여 문자열 배열 strings를 정렬합니다. 
//정렬 기준은 Comparator 인터페이스의 compare 메서드를 오버라이드하여 구현합니다.

//Arrays.sort 메서드
//Arrays.sort(Object[] a, Comparator<? super T> c)는 주어진 배열 a를 
//주어진 Comparator가 제공하는 순서에 따라 정렬합니다. 
//이 경우 배열 a는 문자열 배열 strings이고, Comparator는 익명 클래스로 제공됩니다.

//Comparator<String> 익명 클래스
//new Comparator<String>() { ... }는 Comparator 인터페이스의 익명 구현체를 생성합니다. 
//이 구현체는 문자열 두 개를 비교하는 로직을 제공합니다.

//compare 메서드 오버라이드
//public int compare(String s1, String s2) 메서드는 두 문자열 s1과 s2를 비교하여 
//정렬 순서를 결정합니다. 
//이 메서드는 정수를 반환하며, 반환 값은 다음과 같은 의미를 가집니다:
//음수: s1이 s2보다 앞에 오도록 정렬합니다.
//0: s1과 s2의 순서를 변경하지 않습니다.
//양수: s1이 s2보다 뒤에 오도록 정렬합니다.

//비교 로직
//n번째 문자 비교: s1.charAt(n) != s2.charAt(n)을 사용하여 두 문자열의 n번째 문자를 비교합니다. 
//문자가 서로 다른 경우, 
//이 두 문자의 ASCII 값 차이(s1.charAt(n) - s2.charAt(n))를 반환하여 정렬 순서를 결정합니다.
//전체 문자열 사전순 비교: n번째 문자가 동일한 경우, s1.compareTo(s2)를 사용하여 
//두 문자열 전체를 사전순으로 비교합니다. 
//이는 두 문자열이 사전순으로 어떤 순서로 정렬될지 결정합니다.
//반환 값
//정렬된 문자열 배열 strings를 반환합니다. 이 배열은 n번째 문자를 기준으로 
//오름차순으로 정렬되었으며, n번째 문자가 같은 경우 전체 문자열의 사전순으로 정렬되었습니다.
  • 이 접근 방식은 strings 배열에 대한 정렬을 단 한 번만 수행합니다. 정렬 시, n번째 문자를 기준으로 하되, 이 문자가 동일한 경우 전체 문자열의 사전순으로 정렬합니다.
  • Comparator 인터페이스를 사용하여 사용자 정의 정렬 기준을 적용합니다. 이를 통해, 정렬 로직이 명확하게 표현되고, 코드의 가독성과 효율성이 모두 향상됩니다.
  • 원래 코드에서 발생할 수 있는 로직의 오류와 비효율성을 해결하며, 문제의 요구사항을 정확히 충족시킵니다.

 

 

 

38. K번째 수

import java.util.stream.IntStream;

class Solution {
    public int[] solution(int[] array, int[][] commands) {
        int[] answer = new int[commands.length];
        for (int i = 0; i < commands.length; i++) {
            int idx1 = commands[i][0]-1;
            int idx2 = commands[i][1]-1;
            int idx3 = commands[i][2]-1;
            int[] cutArray = IntStream.rangeClosed(idx1, idx2)
                    .map(n -> array[n]).sorted().toArray();
            answer[i] = cutArray[idx3];
        }
        return answer;
    }
}

 

코드 리뷰

  • 가독성
    • 코드 이해: 코드는 비교적 간결하고, 주어진 문제의 로직을 잘 따르고 있습니다. IntStream을 사용하여 배열의 특정 부분을 선택하고 정렬하는 과정은 스트림 API의 특성을 잘 활용한 좋은 예입니다.
    • 변수명: 변수명 idx1, idx2, idx3는 각각 commands의 시작 인덱스, 끝 인덱스, 반환할 위치의 인덱스를 나타냅니다. 이 변수명들은 명확하지만, 더 의미 있는 이름을 사용할 수도 있습니다(예: start, end, kth).
  • 성능
    • 스트림 API 사용: 스트림 API는 강력하고 표현력이 풍부하지만, 대량의 데이터를 처리할 때는 기존의 반복문에 비해 성능이 떨어질 수 있습니다. 그러나 이 코드에서 처리해야 하는 데이터 양은 비교적 작으므로 큰 문제가 되지 않을 것입니다.
    • 부분 배열 생성: IntStream.rangeClosed와 map을 사용하여 매번 새로운 배열을 생성하고 정렬합니다. 이 과정은 필요한 계산보다 더 많은 자원을 사용할 수 있습니다.
  • 확장성
    • 코드는 주어진 문제의 요구사항을 충족합니다. 확장성 측면에서, 비슷한 유형의 배열 처리나 범위 기반의 쿼리에 쉽게 적용될 수 있습니다. commands 배열을 다루는 방식은 다양한 배열 처리 작업에 유용할 수 있습니다.
  • 유지보수
    • 유지보수 용이성: 코드는 간결하고, 사용된 로직은 이해하기 쉽습니다. 필요한 경우 IntStream을 사용하는 부분을 수정하여 성능을 개선하거나 다른 로직을 적용하기 쉽습니다.
    • 예외 처리: 코드에는 입력 값에 대한 명시적인 예외 처리가 없습니다. 예를 들어, commands 배열의 각 원소가 유효한 범위를 가지는지 확인하는 로직을 추가할 수 있습니다.

개선 코드

import java.util.Arrays;

class Solution {
    public int[] solution(int[] array, int[][] commands) {
        int[] answer = new int[commands.length];
        for (int i = 0; i < commands.length; i++) {
            int start = commands[i][0] - 1;
            int end = commands[i][1];
            int kth = commands[i][2] - 1;
            
            int[] tempArray = Arrays.copyOfRange(array, start, end);
            Arrays.sort(tempArray);
            answer[i] = tempArray[kth];
        }
        return answer;
    }
}
  • Arrays.copyOfRange와 Arrays.sort를 사용하여 각 명령에 따라 배열을 잘라내고 정렬합니다. 이 방식은 IntStream을 사용하는 것보다 전통적이지만, 작업의 의도를 명확하게 표현하고, 성능에도 충분히 효율적입니다.
  • 변수명을 start, end, kth로 변경하여, 각각이 무엇을 의미하는지 더 명확하게 합니다.

 

 

 

39. 두 개 뽑아서 더하기

import java.util.ArrayList;
import java.util.List;

class Solution {
    public int[] solution(int[] numbers) {
        //배열의 두 수를 합친 값을 넣을 리스트
        List<Integer> numList = new ArrayList<>();
        //배열의 두 수의 모든 조합 계산 후 리스트에 추가
        for (int i = 0; i < numbers.length; i++) {
            for (int j = i + 1; j < numbers.length; j++) {
                int sum = numbers[i] + numbers[j];
                numList.add(sum);
            }
        }
        //리스트의 중복 값 제거 후 오름차순 정렬 후 배열로 반환
        int[] answer = numList.stream().mapToInt(i -> i).distinct().sorted().toArray();

        return answer;

    }
}

 

코드 리뷰

  • 가독성
    • 코드 이해: 코드는 비교적 직관적입니다. 두 개의 중첩된 for 반복문을 사용하여 배열 내의 모든 가능한 수의 조합을 계산하고, 이를 리스트에 저장합니다. 이후, 스트림 API를 사용하여 중복을 제거하고 결과를 오름차순으로 정렬합니다.
    • 변수명: numList는 합계를 저장하는 용도로 사용되며, 변수명이 그 목적을 잘 나타냅니다.
  • 성능
    • 중복 검사의 효율성: 리스트에 합계를 추가할 때 중복 검사를 하지 않고, 나중에 스트림 API의 distinct() 메서드를 사용하여 중복을 제거합니다. 이는 중복 값이 많을 경우 비효율적일 수 있습니다. 중복 체크를 더 효율적으로 처리할 수 있는 방법이 있습니다.
    • 스트림 API 사용: 스트림 API는 강력하고 코드를 간결하게 만들어 주지만, 큰 데이터 세트를 처리할 때는 기존의 for 반복문에 비해 성능이 떨어질 수 있습니다. 이 경우 성능 차이가 크게 나지 않을 수 있으나, 대규모 데이터에 대한 처리에서는 차이가 나탈 수 있습니다.
  • 확장성
    • 이 코드는 특정 문제에 대한 해결책으로 설계되었으며, 비슷한 유형의 문제(예: 조합의 결과를 계산하고 정렬하는 문제)에 재사용될 수 있습니다.
  • 유지보수
    • 유지보수 용이성: 코드는 간결하고 명확하여 유지보수가 쉬울 것으로 보입니다. 필요에 따라 중복 제거 방식이나 정렬 로직을 수정하기도 쉽습니다.

개선 코드

import java.util.Set;
import java.util.HashSet;
import java.util.Arrays;

class Solution {
    public int[] solution(int[] numbers) {
        Set<Integer> numSet = new HashSet<>();
        for (int i = 0; i < numbers.length; i++) {
            for (int j = i + 1; j < numbers.length; j++) {
                numSet.add(numbers[i] + numbers[j]);
            }
        }
        int[] answer = numSet.stream().mapToInt(i -> i).sorted().toArray();
        return answer;
    }
}
  • HashSet을 사용하여 중복 값을 자동으로 제거합니다. 이는 중복 값을 리스트에 추가하는 것보다 더 효율적으로 중복을 관리할 수 있게 합니다.
  • 중복 제거 과정을 스트림 API를 사용하기 전에 처리하여, 스트림의 distinct() 호출을 제거함으로써 성능을 개선합니다.
  • 결과적으로, 이 개선안은 중복 처리를 더 효율적으로 하면서도 문제의 요구사항을 만족시키는 해결책을 제공합니다.

 

 

 

페어 프로그래밍 코드 (22~35)

 

22.

package pair3;

/* 내적
 * 길이가 같은 두 1차원 정수 배열 a, b가 매개변수로 주어집니다.
 * a와 b의 내적을 return 하도록 solution 함수를 완성해주세요.
 * */
public class DotProduct {
    private static int solution(int[] a, int[] b) {
        int answer = 0;
        for (int i = 0; i < a.length; i++) {
            answer += a[i] * b[i];
        }
        return answer;
    }

    public static void main(String[] args) {
        System.out.println();
    }
}

 

 

 

23.

package pair3;

/* 약수의 개수와 덧셈
 * 두 정수 left와 right가 매개변수로 주어집니다.
 * left부터 right까지의 모든 수들 중에서, 약수의 개수가 짝수인 수는 더하고, 약수의 개수가 홀수인 수는 뺀 수를 return 하도록 solution 함수를 완성해주세요.
 * */
public class Factors {
    private static int solution(int left, int right) {
        int answer = 0;
        for (int i = left; i <= right; i++) {
            answer += (i % Math.sqrt(i) == 0) ? -i : i;
        }

        return answer;
//        int sum = IntStream.rangeClosed(left, right).sum();
//
//        int sqrt = (int) Math.ceil(Math.sqrt(left));
//        for (int i = sqrt; i * i <= right; i++) {
//            sum -= 2 * i * i;
//        }
//
//        return sum;

    }

    public static void main(String[] args) {
        System.out.println(Factors.solution(13, 17));
    }
}

 

 

 

24.

package pair3;

import java.util.Comparator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/* 문자열 내림차순 배치하기
 * 문자열 s에 나타나는 문자를 큰것부터 작은 순으로 정렬해 새로운 문자열을 리턴하는 함수, solution을 완성해주세요.
 * */
public class DescendingStr {
    private static String solution(String s) {
//        char[] arr = s.toCharArray();
//        Arrays.sort(arr);
//
//        return new StringBuilder(new String(arr)).reverse().toString();

        return Stream.of(s.split("")).
                sorted(Comparator.reverseOrder()).
                collect(Collectors.joining());
    }

    public static void main(String[] args) {

    }
}

 

 

 

25.

package pair3;

/* 부족한 금액 계산하기
 * 처음 이용료가 100이었다면 2번째에는 200, 3번째에는 300으로 요금이 인상됩니다.
 * 놀이기구를 count번 타게 되면 현재 자신이 가지고 있는 금액에서 얼마가 모자라는지를 return 하도록 solution 함수를 완성하세요.
 * 단, 금액이 부족하지 않으면 0을 return 하세요.
 */
public class Amount {
    private static long solution(int price, int money, int count) {
        long answer = 0L;
        long bigMoney = (long) money;
        for (int i = 1; i <= count; i++) {
            answer += price * i;
        }

        if (bigMoney >= answer) return 0;

        return answer - bigMoney;

//        long answer = LongStream.rangeClosed(1,count)
//                .map(n -> n * price).sum() - money;
//
//        if (answer <= 0) {
//            answer = 0;
//        }
//        return answer;
    }

    public static void main(String[] args) {

    }
}

 

 

 

26.

package pair3;

/* 문자열 다루기 기본
 * 문자열 s의 길이가 4 혹은 6이고, 숫자로만 구성돼있는지 확인해주는 함수, solution을 완성하세요.
 * */
public class StringHandling {
    private boolean solution(String s) {
//        boolean answer = true;
//        if (s.length() != 4 && s.length() != 6) {
//            return false;
//        }
//
//        char[] arr = s.toCharArray();
//        for (int i = 0; i < arr.length; i++) {
//            if (!(arr[i] <= '9')) answer = false;
//        }
//
//        return answer;

        if (s.length() == 4 || s.length() == 6) {
            return s.matches("[0-9]+") ? true : false;
        }
        return false;
    }

    public static void main(String[] args) {

    }
}

 

 

 

27.

package pair3;

/* 행렬의 덧셈
 * 행렬의 덧셈은 행과 열의 크기가 같은 두 행렬의 같은 행, 같은 열의 값을 서로 더한 결과가 됩니다.
 * 2개의 행렬 arr1과 arr2를 입력받아, 행렬 덧셈의 결과를 반환하는 함수, solution을 완성해주세요.
 * */
public class MatrixAdd {
    private static int[][] solution(int[][] arr1, int[][] arr2) {
        int[][] answer = new int[arr1.length][arr1[0].length];

        for (int i = 0; i < arr1.length; i++) {
            for (int j = 0; j < arr1[0].length; j++) {
                answer[i][j] = arr1[i][j] + arr2[i][j];
            }
        }

        return answer;
    }

    public static void main(String[] args) {

    }
}

 

 

 

28.

package pair3;

import java.util.Scanner;

/* 직사각형 별찍기 */
public class RectangularStar {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt(); // 5
        int b = sc.nextInt(); // 3

        for (int i=0; i<b; i++) {
            for (int j = 0; j < a; j++) {
                System.out.print("*");
            }
            System.out.println();
        }
    }
}

 

 

 

29.

package pair3;

/* 최대공약수와 최소공배수
 * 두 수를 입력받아 두 수의 최대공약수와 최소공배수를 반환하는 함수, solution을 완성해 보세요.
 * */
public class DivisorMultiple {
    public int[] solution(long n, long m) {
        int[] answer = new int[2];

        // n, m 크기 비교
        long max = Math.max(n, m);
        long min = Math.min(n, m);

        // 작은 수 크기 만큼 1씩 감소 시키면서 두 수 모두 나머지가 0이 되는 수 찾은 후 break => 최대공약수
        long divisor = 0L;
        for (long i = min; i > 0; i--) {
            if (n % i == 0 && m % i ==0) {
                divisor = i;
                answer[0] = (int) divisor;
                break;
            }
        }

        // n / 최대공약수 * m / 최대공약수 * 최대공약수 => 최소공배수
        answer[1] = (int) (divisor * (n / divisor) * (m / divisor));
        return answer;

        //유클리드 호제법
//        int[] result = {gcd(n, m), lcm(n,m)}
    }

//    //최대 공약수 구하기
//    public static int gcd(int a, int b) {
//        if (b == 0) return a;
//        return gcd(b, a % b);
//    }
//
//    //최소 공배수 구하기
//    public static int lcm(int a, int b) {
//        return a * b / gcd(a, b);
//    }
//
//    public static int gcdLoop(int a, int b) {
//        while (b != 0) {
//            int temp = b;
//            b = a % b;
//            a = temp;
//        }
//        return a;
//    }

    public static void main(String[] args) {

    }
}

 

 

 

30.

package pair3;

/* 3진법 뒤집기 */
public class TernaryReverse {
    private static int solution(int n) {
        String s = new StringBuilder(Integer.toString(n, 3)).reverse().toString();
        return Integer.parseInt(s, 3);
    }

    public static void main(String[] args) {

    }
}

 

 

 

31.

package pair3;

/* 이상한 문자 만들기
 * 문자열 s는 한 개 이상의 단어로 구성되어 있습니다.
 * 각 단어는 하나 이상의 공백문자로 구분되어 있습니다.
 * 각 단어의 짝수번째 알파벳은 대문자로, 홀수번째 알파벳은 소문자로 바꾼 문자열을 리턴하는 함수, solution을 완성하세요.
 * */
public class CreateStrangeChar {
    private static String solution(String s) {
        s = s.toLowerCase();
        char[] arr = s.toCharArray(); // [t,r,y, ,h,e,l,l,o, ,w,o,r,l,d]

        int cnt = 1;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == ' ') {
                cnt = 1;
                continue;
            }

            if (!(cnt % 2 == 0)) {
                arr[i] = Character.toUpperCase(arr[i]);
            }

            cnt++;
        }

        return new String(arr);

//        String answer = "";
//        int cnt = 0;
//        String[] str = s.split("");
//        for (String word : str) {
//            cnt = (word.contains(" ")) ? 0 : cnt + 1;
//            answer += (cnt % 2 == 0) ? word.toLowerCase() : word.toUpperCase();
//        }
//        return answer;

    }

    public static void main(String[] args) {
        System.out.println(CreateStrangeChar.solution("try hello world"));
    }
}

 

 

 

32.

package pair3;

/* 삼총사
 * */
public class Three {
    private static int solution(int[] numbers) {
        int answer = 0;

        for (int i = 0; i < numbers.length - 2; i++) {
            for (int j = i+1; j < numbers.length - 1; j++) {
                for (int k =j+1; k < numbers.length; k++) {
                    int sum = numbers[i] + numbers[j] + numbers[k];
                    if (sum == 0) answer++;
                }
            }
        }

        return answer;
    }

    public static void main(String[] args) {

    }
}

 

 

 

33.

package pair3;

/* 크기가 작은 부분 문자열
 * 숫자로 이루어진 문자열 t와 p가 주어질 때,
 * t에서 p와 길이가 같은 부분문자열 중에서,
 * 이 부분문자열이 나타내는 수가 p가 나타내는 수보다 작거나 같은 것이 나오는 횟수를 return하는 함수 solution을 완성하세요.
 * */
public class SmallSubstring {
    private static int solution(String t, String p) {
        int answer = 0;
        long longP = Long.parseLong(p);

        // 반복문을 이용하여
        // t를 p의 길이만큼 자른 후 배열에 넣기
        int len = t.length() - p.length() + 1;
        String[] arr = new String[len];
        for (int i = 0; i < len; i++) {
            arr[i] = t.substring(i, i + p.length());
            long longArr = Long.parseLong(arr[i]);
            answer += (longArr <= longP) ? 1 : 0;
        }

        return answer;
//        int cnt = 0;
//        int strLength = t.length() - p.length();
//        for (int i = 0; i <= strLength; i++) {
//            String part = t.substring(i, i + p.length());
//            cnt += (Long.parseLong(part) <= Long.parseLong(p)) ? 1 : 0;
//        }
//        return cnt;

    }

    public static void main(String[] args) {

    }
}

 

 

 

34.

package pair3;

import java.util.Arrays;

/* 최소 직사각형
 * */
public class MinimumRectangle {
    private static int solution(int[][] sizes) {
        // 오름차순 정렬
        for (int i = 0; i < sizes.length; i++) {
            Arrays.sort(sizes[i]);
        }

        int a = 0; // 0번 인덱스에서 가장 큰 수 저장
        int b = 0; // 1번 인덱스에서 가장 큰 수 저장
        for (int i = 0; i < sizes.length; i++) {
            if (sizes[i][0] > a) a = sizes[i][0];
            if (sizes[i][1] > b) b = sizes[i][1];
        }

        return a * b;

//        int max1 = Arrays.stream(sizes).mapToInt(arr -> arr[0]).max().orElse(0);
//        int max2 = Arrays.stream(sizes).mapToInt(arr -> arr[1]).max().orElse(0);
//        return max1 * max2;
    }

    public static void main(String[] args) {

    }
}

 

 

 

35.

package pair3;

/* 시저 암호
 * 어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다.
 * 예를 들어 "AB"는 1만큼 밀면 "BC"가 되고, 3만큼 밀면 "DE"가 됩니다.
 * "z"는 1만큼 밀면 "a"가 됩니다. 문자열 s와 거리 n을 입력받아 s를 n만큼 민 암호문을 만드는 함수, solution을 완성해 보세요.
 * */
public class Caesar {
    private static String solution(String s, int n) {
/*        char[] arr = s.toCharArray();

        for (int i=0; i<arr.length; i++) {
            if (arr[i] == ' ') continue;

            if (Character.isLowerCase(arr[i])) {
                arr[i] = (char) (arr[i] + n);
                if (arr[i] > 'z') {
                    arr[i] = (char) (arr[i] - 26);
                }
            } else {
                arr[i] = (char) (arr[i] + n);
                if (arr[i] > 'Z') {
                    arr[i] = (char) (arr[i] - 26);
                }
            }
        }

        return new String(arr);*/

        String answer = "";

        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (Character.isLowerCase(ch)) {
                ch = (char) ((ch - 'a' + n) % 26 + 'a');
            } else if (Character.isUpperCase(ch)) {
                ch = (char) ((ch - 'A' + n) % 26 + 'A');
            }
            answer += ch;
        }
        return answer;
    }

    public static void main(String[] args) {

    }
}

//(ch - 'a'): 문자 ch의 ASCII 코드에서 문자 'a'의 ASCII 코드를 빼서, ch가 알파벳에서 몇 번째 위치에 있는지(0부터 시작)를 계산합니다.
//+ n: 계산된 위치에 n을 더해, ch를 알파벳상에서 n만큼 이동시킵니다.
//% 26: 알파벳은 26개의 문자로 구성되어 있으므로, 이동시킨 결과가 알파벳 범위를 초과하지 않도록 26으로 나눈 나머지를 구합니다. 이는 알파벳을 순환시키는 효과를 가집니다.
//+ 'a': 마지막으로 'a'의 ASCII 코드를 더해주어, 계산된 위치를 다시 ASCII 코드로 변환합니다. 이는 순환된 위치의 실제 문자를 얻기 위함입니다.
//예를 들어, ch가 'a'이고 n이 3이라면, 계산 결과는 다음과 같습니다:
//'a'의 ASCII 코드는 97입니다.
//(ch - 'a')는 97 - 97 = 0입니다.
//+ n은 0 + 3 = 3입니다.
//% 26은 3 % 26 = 3입니다. (알파벳 범위 내이므로 변경 없음)
//+ 'a'는 3 + 97 = 100입니다.
//따라서 최종 결과는 ASCII 코드 100에 해당하는 문자 'd'가 됩니다. 즉, 'a'에서 3글자 뒤는 'd'입니다.

 

 

 

 

멘토링 질문 사항

  • 코딩 테스트 연습 - 프로그래머스 문제만 다 풀면 되는지?
  • 개발 공부할 때 좋은 방법?