콘솔창 & 윈도우창/코딩 테스트

프로그래머스 LV.1 신규 아이디 추천

뽀또치즈맛 2024. 11. 13. 13:43

https://school.programmers.co.kr/learn/courses/30/lessons/72410

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

 

 

해당 문제는 결국 규칙에 맞는 아이디를 추천하는 것이다.

아이디 규칙은 다음과 같다.

 

아이디의 제한 사항은 다음과 같다.

 

 

string solution(string new_id) {
    string answer = "";

    // 소문자 치환
    // 레퍼런스는 포인터를 쓰는 것과 같음.
    for (char& ch : new_id) {
        ch = tolower(ch);
    }

    // 숫자, 빼기, 밑줄, 마침표
    for (char ch : new_id) {
        if (isdigit(ch) ||
            isalpha(ch) ||
            // ch가 "-_." 중 하나인지 확인하는 조건문
            strchr("-_.", ch)) {
            answer += ch;
        }
    }

    int idx = -1;
    // "answer" 문자열에서 연속된 마침표("..")를 하나의 마침표(".")로 바꾸는 반복문
    while ((idx = answer.find("..")) != -1) { // answer 문자열에서 ".."을 찾고, 찾은 위치를 idx에 저장 (없으면 -1 반환)
        answer.replace(idx, 2, "."); // idx 위치에서 2개의 문자("..")를 "."으로 대체
    }

    // .가 처음이나 끝에 위치할 경우
    if (answer.front() == '.') answer = answer.substr(1);
    if (answer.back() == '.') answer = answer.substr(0, answer.length() - 1);

    // 빈 문자열이라면? a 대입
    if (answer.empty())
        answer += "a";
    // 길이가 16자 이상이라면?
    if (answer.size() >= 16) {
        answer = answer.substr(0, 15);
        // 마지막의 문자가 . 이라는 경우의 예외처리
        if (answer.back() == '.')
            answer.pop_back();
    }

    // 길이가 두 자 이하라면?
    while(answer.size() < 3)
        answer += answer.back();
    

    return answer;
}

 

내가 풀이한 코드의 성능

 

 

다 푼 뒤에

훑어보다가 발견한 다른 사람의 코드

 

참고할 만 하다.

가독성이 좋다.

 

#include <bits/stdc++.h>
using namespace std;

string solution(string new_id) {
    for (char& ch : new_id) if ('A' <= ch && ch <= 'Z') ch |= 32;

    string ret;
    for (char& ch: new_id) {
        if ('a' <= ch && ch <= 'z' 
||
            '0' <= ch && ch <= '9' 
||
            strchr("-_.", ch)) ret += ch;
    }

    new_id = ret;
    ret.clear();
    for (char& ch: new_id) {
        if (!ret.empty() && ret.back() == '.' && ch == '.') continue;
        ret += ch;
    }

    if (ret.front() == '.') ret.erase(ret.begin());
    if (ret.back() == '.') ret.pop_back();

    if (ret.empty()) ret = "a";
    if (ret.size() >= 16) ret = ret.substr(0, 15);
    if (ret.back() == '.') ret.pop_back();
    while (ret.size() <= 2) ret += ret.back();

    return ret;
}

 

해당 코드의 성능

 

 

추가

정규표현식을 사용 풀이

string solution(string new_id) {
    string answer = "";

    // 1단계: 모든 문자를 소문자로 변환
    for (char& ch : new_id) {
        ch = tolower(ch);
    }

    // 2단계: 소문자, 숫자, 빼기, 밑줄, 마침표 제외한 모든 문자 제거
    answer = regex_replace(new_id, regex("[^a-z0-9-_.]"), "");

    // 3단계: 마침표가 2번 이상 연속된 부분을 하나의 마침표로 변환
    answer = regex_replace(answer, regex("\\.+"), ".");

    // 4단계: 문자열 처음이나 끝에 있는 마침표 제거
    answer = regex_replace(answer, regex("^[.]|[.]$"), "");

    // 5단계: 빈 문자열이라면 "a" 대입
    if (answer.empty())
        answer = "a";

    // 6단계: 길이가 16자 이상이면 첫 15개 문자만 남기고, 끝에 마침표가 있다면 제거
    if (answer.size() > 15) {
        answer = answer.substr(0, 15);
        answer = regex_replace(answer, regex("[.]$"), "");
    }

    // 7단계: 길이가 2자 이하라면 길이가 3이 될 때까지 마지막 문자 반복
    while (answer.size() < 3)
        answer += answer.back();

    return answer;
}

 

정규 표헌식 성능

 regex_replace 설명

  1. regex_replace(new_id, regex("[^a-z0-9-_.]"), "")
    • 정규 표현식 [^a-z0-9-_.]은
      소문자 알파벳, 숫자, 하이픈(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 의미.
    • 이 부분을 빈 문자열 ""로 대체하여 제거.
  2. regex_replace(answer, regex("\\.+"), ".")
    • 정규 표현식 \\.+는 하나 이상의 마침표 .를 의미.
    • 연속된 마침표들을 하나의 마침표로 변환.
  3. regex_replace(answer, regex("^[.]|[.]$"), "")
    • 정규 표현식 ^[.]|[.]$는 문자열 시작 또는 끝에 있는 마침표 .를 의미.
    • 문자열의 처음이나 끝에 위치한 마침표를 제거.
  4. regex_replace(answer, regex("[.]$"), "")
    • 이 부분은 answer.substr(0, 15) 
      이후 문자열 끝에 남아 있는 마침표를 제거하기 위해,
      정규 표현식 [.]$를 사용해 끝에 위치한 마침표를 찾아서 제거.

정규 표현식을 사용하면 여러 문자나 패턴을 간결하게 처리할 수 있어서 이 문제에 유용하다.

 

성능 차이

정규표현식 > 남의 코드 > 첫제출 코드

 

코딩테스트를 풀기 위해

정규표현식을 알아두면 좋을 것 같다.