문제
: 입력한 문자열이 회문이면 "YES"를 , 회문이 아니면 "NO"를 출력하라
(이때 입력한 문자열은 알파벳 뿐만 아니라 다른 문자도 포함될 수 있으니,
이중 알파벳만을 가지고 회문을 검사하고, 그때 대소문자는 구분하지 말아라)
회문을 영어로 팰린드롬이라고 하는 만큼, 이 문제는 앞선 1.7 회문 문자열 문제와 거의 비슷하다. 즉 "입력한 문자열의 회문 여부 파악" 이 이 문제의 요구사항이라 할 수 있다.
다만 몇가지 핵심 요구사항은 유지한 채 , 한가지 조건이 더 추가되었다고 볼 수 있는데 바로 이것이다.
이 조건이 추가됨에 따라 해결로직 또한 한가지 절차가 더 추가되었다.
1. 대소문자 구분을 무시하기 위해, 전체 문자열을 대문자로 변환
- toUpperCase() 사용
2. 이후 문자열 중 대문자 알파벳만을 색출하여 별도의 문자열을 만듬
(추가된 로직)
: 이 부분에서 나와 , 선생님의 구현 방식에 차이가 있었다.
1) 나의 방식
: 일차원적으로 대문자로 변환한 문자열에 대해서, 대문자 알파벳의 아스키코드값이 65~90라는 점을 기반으로 하여 , 대문자 char만 추출하여 String으로 만듦.
-> 아스키코드값을 이용하여 직관적으로 생각할 수 있지만
-> String을 char배열로 변환 , 결과를 담을 또다른 char 배열, 이를 다시 String으로 변환 해야 하는 추가적인 작업이 필요함. (결국 코드 지저분)
2) 선생님 방식
: String 클래스의 replaceAll() 인스턴스 메서드를 사용하여 대문자만을 직관적으로 뽑아냄
-> 덕분에 코드가 직관적이게 됨. (가독성이 올라감)
3. 이후 만들어진 문자열을 뒤집고 , 기존 문자열과 같은지 여부로, 회문 여부 파악
- StringBuilder 클래스의 reverse() 메서드 로 뒤집고
- String 클래스의 오버라이딩 된 equals() 메서드로 문자열 일치 여부 판단
위 해결 로직에 따른 나의 코드와, 선생님의 코드는 아래와 같다.
i ) 내 코드
import java.util.Scanner;
public class Main {
public static String solution(String str){
//1_1. 전체 문자열을 일단 대문자로 통일
/** 주목할 점: toUpperCase() 사용시 알아서 영어알파벳만 인식하여 대문자로 바꿔주고 , 그외 문자는 그냥 냅둔다.*/
String upperStr = str.toUpperCase();
//1_2. 이후 문자열을 char 배열로 변환
char[] charArr = upperStr.toCharArray();
//2_1. 각 문자별로 대문자만 추출하여 char 배열로 만들고
char[] upperCharrArr = new char[charArr.length];
int idx = 0;
for (char c : charArr) {
if(c>=65 && c<=90)
upperCharrArr[idx++] = c;
}
// 이를 다시 String으로 변환
String str1 = "";
for(int i=0; i<idx; i++){
str1 += upperCharrArr[i];
}
//3_1. 변환한 String을 역전시킨 문장을 만든 후
String str2 = new StringBuilder(str1).reverse().toString();
//3_2. 그 역전시킨 문장과 , 2_1 결과 문장이 일치하면 YES, 그렇지 않은면 NO를 리턴
if(str1.equals(str2)) return "YES";
else return "NO";
}
public static void main(String[] args){
//0. Scanner 준비
Scanner sc = new Scanner(System.in);
//1. 문자열 입력 (next()는 공백을 포함한 문자열을 입력받지 못하고 , nextLine()은 공백을 포함한 문자열을 입력받을 수 있다.)
String str = sc.nextLine();
//2. 입력받은 문자열을 인자로 넘기면서 solution()을 호출하여 , 유효한 팰린드롬인지 여부를 리턴받음
String result = solution(str);
//3. 리턴받은 결과를 출력
System.out.println(result);
}
}
ii) 선생님 코드
import java.util.Scanner;
public class Main2 {
public static String solution(String str){
//1. 알파벳만 인식하여 대문자로 변환시킨 후 -> replaceAll정규식을 이용하여 대문자가 아닌 문자는 빈문자로 대체 -> 즉 제거
/** 주목할 점: toUpperCase() 사용시 알아서 영어알파벳만 인식하여 대문자로 바꿔주고 , 그외 문자는 그냥 냅둔다.*/
String strOrigin = str.toUpperCase().replaceAll("[^A-Z]", "");
//2. 이후 대문자만 남은 문자열을 역전
String strReverse = new StringBuilder(strOrigin).reverse().toString();
//3. 그 역전시킨 문장과 , 2_1 결과 문장이 일치하면 YES, 그렇지 않은면 NO를 리턴
if(strOrigin.equals(strReverse)) return "YES";
else return "NO";
}
public static void main(String[] args) {
//0. Scanner 준비
Scanner sc = new Scanner(System.in);
//1. 문자열 입력 (next()는 공백을 포함한 문자열을 입력받지 못하고 , nextLine()은 공백을 포함한 문자열을 입력받을 수 있다.)
String str = sc.nextLine();
//2. 입력받은 문자열을 인자로 넘기면서 solution()을 호출하여 , 유효한 팰린드롬인지 여부를 리턴받음
String result = solution(str);
//3. 리턴받은 결과를 출력
System.out.println(result);
}
}
이를 통해 새로 알게된 점, 또는 내가 기억해야할 점을 아래와 같이 정리하였다.
1. Scanner의 next()와 nextLine()의 차이
- next()는 공백을 포함한 문자열을 입력받을 수 없고(공백 기준으로 잘림)
- nextLine()은 공백을 포함한 문자열을 입력받을 수 있음.
2. String의 toUpperCase()메서드가 , 문자열을 대문자로 만드는 방식
- 알파벳과 알파벳이 아닌 문자가 섞여 있어도
- 그중에서 알파벳만을 대문자로 변환한다. (확인해보았음)
3. String값 + String이 아닌 값의 덧셈 (+ 기호 사용)
- String이 아닌 값이 String이 되어 , 기존 String뒤에 concat()으로 이어붙여진다.
- String에 char 문자값이 더해지는게 모호했는데, 바로 이 동작방식 덕분에 무사히 문자열로 만들어 지는 것 같다.
4. String 클래스의 인스턴스 메서드로써 replace()와 replaceAll() 이란게 지원된다.
- replace("param1", "param2")
: 해당 문자열에서 "param1"을 찾아서 "param2"로 대체한다
-> 이때 "param2"가 ""로 빈 문자열이면 -> 해당 'param1" 부분이 없어진다.- replaceAll("param1", "param2")
: 해당 문자열에서, "param1"에 해당하는 정규직이 의미하는 불특정 문자열을 전부 - "param2"로 대체한다. (""인경우 모두 없어짐)
-> 이때 선생님이 사용하진 "[^A-Z]" 라는 정규식은 "A부터 Z까지의 대문자가 아닐 경우" 를 의미하고
-> 이에 따라 "A부터 Z까지의 대문자가 아닌 나머지 문자들은 전부 지워진다."
-> 이 정규식에 대해서 추후 더 공부할 필요가 있다.