Welcome! Everything is fine.
[프로그래머스/Lv.1] 신규 아이디 추천(2021 KAKAO BLIND RECRUITMENT) - Java 본문
📌 문제
📌 풀이
1단계 - new_id의 모든 대문자를 대응되는 소문자로 치환한다.
new_id = new_id.toLowerCase();
2단계 - new_id에서 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거한다.
2단계는 new_id를 문자 하나하나 돌면서 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)에 해당되지 않으면 지웠다.
for (Character c : new_id.toCharArray()) {
if (!(c >= 'a' && c <= 'z') && // 알파벳 소문자가 아니라면
!(Character.isDigit(c)) && // 숫자가 아니라면
c != '-' && c != '_' && c != '.') { // 빼기(-), 밑줄(_), 마침표(.)가 아니라면
new_id = new_id.replace(c.toString(), "");
}
}
다른 코드를 보니 replaceAll과 정규표현식으로 한 줄에 담을 수 있었다.
new_id = new_id.replaceAll("[^a-z0-9._-]", "");
정규표현식 [^a-z0-9._-] 에서
- [ ] 는 문자 선택을 표현하며, 괄호 안에 들어간 것들 중 하나를 의미한다.
- ^ 는 NOT을 표현하며 뒤에 나온 것들을 제외한 문자를 의미한다.
- a-z나 0-9에서 - 는 range를 표현하며 a ~ z 사이의 문자, 0 ~ 9 사이의 숫자를 의미한다.
- ._- 는 빼기(-), 밑줄(_), 마침표(.)를 의미한다. 앞에 ^를 붙였으므로 이 문자에 포함되지 않는 문자를 의미한다.
3단계 - new_id에서 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환한다.
new_id = new_id.replaceAll("[.]{2,}", ".");
정규표현식 [.]{2,} 에서
- [ ] 는 문자 선택을 표현하며, 괄호 안에 들어간 것들 중 하나를 의미한다. 마침표(.)는 [ ] 로 묶어주어야 한다.
- [.]{2,} 는 앞에 나온 문자 마침표(.)가 2번 이상 반복됨을 의미한다.
4단계 - new_id에서 마침표(.)가 처음이나 끝에 위치한다면 제거한다.
new_id의 처음인 new_id.charAt(0)이 마침표(.) 이거나 new_id의 끝인 new_id.charAt(new_id.length() - 1) 이 마침표(.0라면 substring을 이용해 해당 부분을 잘라낸다. 주의할 점은 substring 사용시 인덱스 범위를 벗어나면 에러가 나므로 문자열이 비어있지 않은지 미리 체크해줘야 한다는 점이다.
if (!new_id.isEmpty() && new_id.charAt(0) == '.') {
new_id = new_id.substring(1);
}
if (!new_id.isEmpty() && new_id.charAt(new_id.length() - 1) == '.') {
new_id = new_id.substring(0, new_id.length() - 1);
}
이 부분 역시 정규표현식으로 표현할 수 있었다.
new_id = new_id.replaceAll("^[.][.]$", "");
정규표현식 ^[.][.]$ 에서
- ^[.] 는 문자열의 시작을 말하며, 마침표(.)로 시작됨을 의미한다.
- [.]$ 는 문자열의 종료를 말하며, 마침표(.)로 종료됨을 의미한다.
5단계 - new_id가 빈 문자열이라면, new_id에 "a"를 대입한다.
if (new_id.equals("")) new_id = "a";
혹은 삼항연산자로도 표현할 수 있다.
new_id = new_id.isEmpty() ? "a" : new_id;
6단계 - new_id의 길이가 16자 이상이면, new_id의 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거한다. 만약 제거 후 마침표(.)가 new_id의 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거한다.
7단계 - new_id의 길이가 2자 이하라면, new_id의 마지막 문자를 new_id의 길이가 3이 될 때까지 반복해서 끝에 붙입니다.
6, 7단계를 합쳐 작성한 코드이다. 지금 보니 굳이 else-if 다음에 while문이 위치하지 않고 while문만 사용해도 될 것 같다. 또 문자열 연산 시 StringBuilder를 사용하면 더 빨라질 것이다. 또한 마침표(.)가 new_id의 끝에 위치할 경우에 마침요(.)를 제거하는 코드도 정규표현식을 사용했다면 더 깔끔하고 가독성 있는 코드가 될 수 있을 것이다.
if (new_id.length() >= 16) {
new_id = new_id.substring(0, 15);
} else if (new_id.length() <= 2) {
while (new_id.length() < 3) {
new_id += new_id.charAt(new_id.length() - 1);
}
}
// 마침표(.)가 new_id의 끝에 위치할 경우
if (new_id.charAt(new_id.length() - 1) == '.') {
new_id = new_id.substring(0, new_id.length() - 1);
}
📌 전체 코드
나의 코드
나의 코드는 정답이긴 하지만 아주 순차지향적인 코드이다. 다른 코드를 보고 나니 더 깔끔하고 가독성있게 작성할 수 있다는 사실을 알게되었다.
class Solution {
public String solution(String new_id) {
// 1단계
new_id = new_id.toLowerCase();
// 2단계
for (Character c : new_id.toCharArray()) {
if (!(c >= 'a' && c <= 'z') &&
!(Character.isDigit(c)) &&
c != '-' && c != '_' && c != '.') {
new_id = new_id.replace(c.toString(), "");
}
}
// 3단계
new_id = new_id.replaceAll("[.]{2,}", "."); // []로 .을 감싸주어야 함
// 4단계
if (!new_id.isEmpty() && new_id.charAt(0) == '.') {
new_id = new_id.substring(1);
}
if (!new_id.isEmpty() && new_id.charAt(new_id.length() - 1) == '.') {
new_id = new_id.substring(0, new_id.length() - 1);
}
// 5단계
if (new_id.equals("")) new_id = "a";
// 6, 7단계
if (new_id.length() >= 16) {
new_id = new_id.substring(0, 15);
} else if (new_id.length() <= 2) {
while (new_id.length() < 3) {
new_id += new_id.charAt(new_id.length() - 1);
}
}
if (new_id.charAt(new_id.length() - 1) == '.') {
new_id = new_id.substring(0, new_id.length() - 1);
}
return new_id;
}
}
다른 사람의 코드
내가 아주 깔끔하다고 생각한 코드이다. 찾아보니 빌더 패턴(Builder Pattern)이라고 하는데, 객체의 생성 과정과 표현 방법을 분리해 인스턴스를 만드는 생성 패턴이다. 확실히 이렇게 작성하면 보는 입장에서 훨씬 이해가 잘 되고 나중에 유지보수하기도 쉬울 것 같다.
class Solution {
public String solution(String new_id) {
String s = new KAKAOID(new_id)
.replaceToLowerCase()
.filter()
.toSingleDot()
.noStartEndDot()
.noBlank()
.noGreaterThan16()
.noLessThan2()
.getResult();
return s;
}
private static class KAKAOID {
private String s;
KAKAOID(String s) {
this.s = s;
}
private KAKAOID replaceToLowerCase() {
s = s.toLowerCase();
return this;
}
private KAKAOID filter() {
s = s.replaceAll("[^a-z0-9._-]", "");
return this;
}
private KAKAOID toSingleDot() {
s = s.replaceAll("[.]{2,}", ".");
return this;
}
private KAKAOID noStartEndDot() {
s = s.replaceAll("^[.]|[.]$", "");
return this;
}
private KAKAOID noBlank() {
s = s.isEmpty() ? "a" : s;
return this;
}
private KAKAOID noGreaterThan16() {
if (s.length() >= 16) {
s = s.substring(0, 15);
}
s = s.replaceAll("[.]$", "");
return this;
}
private KAKAOID noLessThan2() {
StringBuilder sBuilder = new StringBuilder(s);
while (sBuilder.length() <= 2) {
sBuilder.append(sBuilder.charAt(sBuilder.length() - 1));
}
s = sBuilder.toString();
return this;
}
private String getResult() {
return s;
}
}
}
나의 코드와 다른 사람의 코드를 비교해보니 다른 사람의 코드가 더 빨랐다. 좀 더 효율적인 코드를 많이 보고 배워야겠다.
'프로그래머스 > Lv.1' 카테고리의 다른 글
[프로그래머스/Lv.1] 소수 만들기(feat.에라토스테네스의 체) - Java (1) | 2024.04.22 |
---|---|
[프로그래머스/Lv.1] 기사단원의 무기 - Java (1) | 2024.04.19 |
[프로그래머스/Lv.1] PCCE 기출문제 10번 / 데이터 분석 - Java (0) | 2024.04.18 |
[프로그래머스/Lv.1] 2016년 - Java (0) | 2024.04.18 |
[프로그래머스/Lv.1] 바탕화면 정리 - Java (0) | 2024.04.16 |