* 객체 지향 프로그래밍의 5가지 설계 원칙 (SOLID)

1. 단일 책임 원칙 : 클래스는 단 하나의 목적을 가져야 하며, 클래스를 변경하는 이유는 단 하나의 이유여야 한다.

2. 클래스는 확장에는 열려 있고, 변경에는 닫혀 있어야 한다.

3. 리스코프 치환 원칙 : 상위 타입의 객체를 하위 타입으로 바꾸어도 프로그램은 일관되게 동작해야 한다.

4. 인터페이스 분리 원칙 : 클라이언트는 이용하지 않는 메서드에 의존하지 않도록 인터페이스를 분리해야 한다.

5. 의존 역전 법칙 : 클라이언트는 추상화(인터페이스)에 의존해야 하며, 구체화(구현된 클래스)에 의존해선 안 된다.


* 객체 지향 프로그래밍의 4가지 특징

객체 지향에는 4가지 특징이 있습니다.

1) 캡슐화

2) 추상화

3) 상속

4) 다형성

'c, c++ 리마인드' 카테고리의 다른 글

상속 작동 방식  (0) 2025.12.17
네임스페이스가 있는 클래스 전방 선언 방법  (0) 2025.01.21
라이브러리 import/export 키워드  (0) 2025.01.20
[c++] 참조자  (1) 2024.06.16
포인터 연산  (0) 2024.05.22

* 컨테이너 (set, map, priority queue) 를 쓸 때, key 를 구조체 같은 특수한 걸로 넣으려면
비교함수(정확히는 비교 함수 객체(functor)) 를 사용한다.

* 해시 테이블 컨테이너(unordered_set / map) 를 쓸 때, 해시 함수(해시 함수 객체) 를 사용한다.
( + 사용 구조체 안에 operator== 정의 )

 

왜?

  1. 컨테이너 (set, priority_queue, map): 꺽쇠 < > 안에 넣으므로 **자료형(Type)**이 필요합니다. → **구조체(Class/Struct)**여야 함. [템플릿 사용 케이스]
  2. 알고리즘 (sort): 괄호 ( ) 안에 넣으므로 **실체(Value/Object)**가 필요합니다. → 함수 그 자체도 가능함. [함수 사용 케이스]

비교 함수 객체

// 1. 데이터 구조체 (무기)
struct Weapon {
    string name;
    int power;
};

// 2. 비교 함수 객체 (Functor) 정의
// 구조체 안에 operator()를 넣어서 함수처럼 동작하게 만듦
struct Comp {
    // const 참조(&)로 받고, 함수 뒤에도 const를 붙이는 것이 정석
    bool operator()(const Weapon& a, const Weapon& b) const {
        
        // 정렬 기준 1: 공격력이 높은게 앞으로 오도록 (내림차순)
        if (a.power != b.power) {
            return a.power > b.power; // 부등호 방향에 주의! (> : 내림차순 효과)
        }
        
        // 정렬 기준 2: 공격력이 같다면 이름 사전순 (오름차순)
        // (set은 겹치는 키를 허용 안 하므로, 구분을 위한 2차 기준이 필수적임)
        return a.name < b.name;
    }
};

int main() {
    // 3. 컨테이너 선언 (자료형, 비교구조체)
    // set 사용법: <자료형, 비교구조체>
    set<Weapon, Comp> mySet;

    // priority_queue 사용법: <자료형, 내부컨테이너(vector), 비교구조체>
    priority_queue<Weapon, vector<Weapon>, Comp> myPQ;

    // 데이터 삽입
    mySet.insert({"낡은칼", 10});
    mySet.insert({"전설의검", 999});
    mySet.insert({"철검", 50});

    // set 출력 (자동 정렬됨)
    cout << "[ Set : 공격력 높은 순 ]" << endl;
    for (const auto& w : mySet) {
        cout << w.name << " (" << w.power << ")" << endl;
    }

    return 0;
}

 

 

해시 함수 객체

#include <unordered_set>
#include <functional> // std::hash 사용 위해

// 1. 노드 구조체 정의
struct Node {
    int x, y;
    // 필요한 경우 f, g, h 비용 변수 등 추가...

    // [필수 1] 동등 비교 연산자 (operator==)
    // 두 노드가 "같은지" 판단할 때 쓰입니다. (해시 충돌 시 확인용)
    bool operator==(const Node& other) const {
        return x == other.x && y == other.y;
    }
};

// 2. 해시 함수 구조체 정의
// 노드를 받아서 "고유한 숫자(size_t)"로 바꿔주는 기계입니다.
struct NodeHash {
    size_t operator()(const Node& node) const {
        // x와 y를 적절히 섞어서 유일한 키를 만듭니다.
        // 단순히 x + y 하면 (1,2)와 (2,1)이 같아지므로 비트 연산 등을 섞습니다.
        return std::hash<int>()(node.x) ^ (std::hash<int>()(node.y) << 1);
    }
};

int main() {
    // 3. 선언 (자료형, 해시함수)
    std::unordered_set<Node, NodeHash> closedSet;

    // 사용
    Node startNode = {10, 20};
    closedSet.insert(startNode);

    if (closedSet.find({10, 20}) != closedSet.end()) {
        std::cout << "이미 방문한 노드입니다!" << std::endl;
    }

    return 0;
}

자식 클래스에서 private 상속했을 시

 

1. 클래스 내부 (자식 입장에서 부모를 볼 때)

"상속 방식(public/protected/private)에 상관없이 부모의 private이 아닌 것들은 그냥 사용된다."

자식 클래스 안에서 코드를 짤 때(Something::func()), 부모 클래스(UnCopyable)의 멤버를 사용할 수 있느냐 없느냐는 오직 부모 클래스에 적힌 접근 지정자(public, protected, private) 에 의해서만 결정됩니다.
상속을 private으로 하든, public으로 하든, 자식은 부모의 public과 protected 멤버를 무조건 마음껏 가져다 쓸 수 있습니다.
상속 키워드는 자식의 눈을 가리지 않습니다.

 

2. 클래스 외부 (Main 함수나 제3자가 자식을 볼 때)

"클래스의 부모의 기능들이 public이라도 main 에서는 못 쓰게 하는 거다."

상속 방식(: private, : protected, : public)은 **"부모에게서 물려받은 재산(멤버)을 외부 사람들에게 어떻게 보여줄 것인가?"**를 결정하는 필터 역할을 합니다.
public 상속: "부모님이 주신 거 다 보여줄게." (부모의 public → 자식의 public)
protected 상속: "부모님이 주신 건 우리 가족(자식과 그 후손)끼리만 쓸게." (부모의 public → 자식의 protected)
private 상속: "부모님이 주신 건 나까지만 쓰고, 내 자식(손자 클래스)이나 외부에는 비밀로 할게." (부모의 public → 자식의 private)

 

class Parent {
public:
    void PublicFunc() {}      // 누구나 사용 가능
protected:
    void ProtectedFunc() {}   // 자식까지만 사용 가능
private:
    void PrivateFunc() {}     // 나만 사용 가능
};

// [private 상속]
// 의미: "부모님의 기능을 구현에만 써먹고(Is-implemented-in-terms-of), 
//       밖에는 우리 관계를 비밀로 하겠다(Not Is-a)."
class Child : private Parent {
public:
    void MyFunction() {
        // [1. 클래스 내부]
        // 상속이 private이어도, 자식은 부모의 public/protected를 다 쓸 수 있음!
        PublicFunc();      // OK
        ProtectedFunc();   // OK
        // PrivateFunc();  // Error: 이건 부모님이 자식에게도 안 준 것
    }
};

class GrandChild : public Child {
public:
    void GrandFunction() {
        // [private 상속의 여파]
        // Child가 Parent의 모든 것을 private으로 바꿔버렸기 때문에 손자는 접근 불가
        // PublicFunc();    // Error!
        // ProtectedFunc(); // Error!
    }
};

int main() {
    Child c;
    
    // [2. 클래스 외부]
    c.MyFunction(); // Child 자신의 public 함수는 당연히 OK
    
    // 부모에게 물려받은 거지만, private 상속 때문에 외부에서는 접근 불가
    // c.PublicFunc();    // Error! (외부에서는 이게 있는지도 모름)
    // c.ProtectedFunc(); // Error!
    
    return 0;
}

'c, c++ 리마인드' 카테고리의 다른 글

OOP (객체 지향)  (0) 2026.06.13
네임스페이스가 있는 클래스 전방 선언 방법  (0) 2025.01.21
라이브러리 import/export 키워드  (0) 2025.01.20
[c++] 참조자  (1) 2024.06.16
포인터 연산  (0) 2024.05.22
namespace A
{
namespace B
{
    class FirstClass
    {
    ...
    }
}

//이렇게 클래스가 있을 때, 다른 클래스에서 위 클래스를 전방선언 하는 법


namespace A::B {class FirstClass;}
class SecondClass
{
....
	A::B::FistClass* member;
}

'c, c++ 리마인드' 카테고리의 다른 글

OOP (객체 지향)  (0) 2026.06.13
상속 작동 방식  (0) 2025.12.17
라이브러리 import/export 키워드  (0) 2025.01.20
[c++] 참조자  (1) 2024.06.16
포인터 연산  (0) 2024.05.22

라이브러리를 만들 때, 해당 전처리기를 쓰고, 클래스 이름 앞에 키워드를 넣어 놓아야

타 프로젝트에서 해당 라이브러리를 사용할때 링크에러 안남.

(단, 라이브러리 DLL 프로젝트에서만 _DLL_EXPORT 를  define 정의한다.
그 DLL을 사용하는 다른 프로젝트에서는 _DLL_EXPORT를 정의하지 않는다.
그러면 자동으로 dllimport로 해석된다.)

 

#ifdef _DLL_EXPORT
	#define DLL_TYPE __declspec (dllexport)
#else if
	#define DLL_TYPE __declspec (dllimport)
#endif

class DLL_TYPE baseClass {
public:
	baseClass();
	~baseClass();

	std::string name;
	virtual void init(std::string name);
	virtual int money();
};
  • 컴파일 할 때, 해당 라이브러리 코드가 import로 사용되는 건지, export로 사용되는 건지 결정하는 것.
  • dllexport 지정이 없으면 DLL 내부의 .cpp에 멤버 함수를 정의했더라도, 해당 심볼이 DLL 밖으로 공개되지 않아 다른 프로그램에서 링크할 수 없다.

'c, c++ 리마인드' 카테고리의 다른 글

상속 작동 방식  (0) 2025.12.17
네임스페이스가 있는 클래스 전방 선언 방법  (0) 2025.01.21
[c++] 참조자  (1) 2024.06.16
포인터 연산  (0) 2024.05.22
멤버 변수, 동적 할당 배열 크기 구하기  (0) 2024.04.12

참조자(reference)상수 / 임시 객체 를 참조 할 수 없다.

  • 참조자가 상수 / 임시 객체를 참조하려면 const 를 붙여야 한다.
  • const 참조함으로서 임시 메모리 공간에 이름을 부여하여 상수(임시 객체) 의 수명이 그 참조가 유효한 동안 연장된다.
template<class T>
class NamedObject
{
public:
    NamedObject(string& name, const T& value) : nameValue(name), objectValue(value) { };

private:
    string nameValue;
    const T& objectValue;
};

int main()
{
    NamedObject<int> p(string("abc"), 2);        //error. 임시 string을 참조할 수 없음.
    
    string name("aaa");
    NamedObject<int> s(name, 34);                //ok
}

 

생성자의 name 매개변수에 const 를 붙이면 임시 string을 참조할 수 있음.

NamedObject(const string& name, const T& value) : nameValue(name), objectValue(value) { };

 

include <tuple>

  • piar 템플릿의 신세대 버전 ( 개라도 담을 있다)

template<typename... Args>

int getCount(std::tuple<Args...> A){

    return std::tuple_size<decltype(A)>::value;

}

 

int getCount(std::tuple<Args...> A) {

    return std::tuple_size<decltype(A)>::value;

}

int main()
{
    // 튜플 정의하기
    tuple<int, char, string> sc1 = std::make_tuple<double, char, std::string>(3, 'A', "김덕배");
    tuple<int, char, string> sc2 = { 4, 'B', "엄준식" };

    cout << get<0>(sc1) << endl;	// 튜플 요소 접근하기
    cout << get<1>(sc2) << endl;

    int a;
    char b;
    string c;
    tie(a, b, c) = sc1;			// 튜플 요소 뽑아오기
    cout << c << endl;

    cout << "s1 size : "<< getCount(sc1) << endl;
}

'유용한 기술' 카테고리의 다른 글

[c++] 정규 표현식 regex  (0) 2024.05.26
[algorithm] transform & inserter  (0) 2024.05.14
(custom sort) Compare 작성  (0) 2024.05.11
C++ sscanf_s  (1) 2023.10.19
c++ algorithm [count, count_if]  (0) 2023.10.11

include <regex>

정규 표현식 기반, 문자열에 대한 탐색/교체 

 

정규 표현식 기호 종류

.  임의의 한 문자를 나타낸다. 예를 들면, a.c → "abc" | "a2c" | "a&c" 등.
*  앞의 요소가 '0'번 이상 반복됨을 나타낸다. 예를 들면, a*  "" | "a" | "aa" 등.
+  앞의 요소가 '1'번 이상 반복됨을 나타낸다. 예를 들면, a*  "a" | "aa" 등. ( "" 은 x)
?  앞의 요소가 0번 또는 1번 나타난다. 예를 들면, a?  "" | "a" 
\\d  숫자를 나타낸다. 0 ~ 9 까지 숫자 문자.
\\w  글자를 나타낸다. 알파벳, 숫자, 밑줄(_)
\\b  단어 경계를 나타낸다. \\b ~~~ \\b  (단어의 시작과 끝을 구분하는 용도)
[xxx..]  대괄호 안에 있는 문자와 일치하는 글자를 나타낸다. 예를 들면, [abc] "a" | "b" | "c".
[^xxx..]  대괄호 안에 있는 문자와 일치하지 않는 글자를 나타낸다. 예를 들면, [^abc]  "a" | "b" | "c" 를 제외한 모든 글자
^x  "x" 로 시작하는 문자열 또는 줄을 나타낸다. 예를 들면, ^a 'a' 로 시작하는 문자열.
$x "x" 로 끝나는 문자열 또는 줄을 나타낸다. 예를 들면, ^a  'a' 로 끝나는 문자열.
{m, n} 앞의 요소가 m번 이상 n번 이하로 포함된다는 것을 나타낸다. ex) a{1, 3}   a가 1번이상, 3번 이하 포함된 문자열
{min} 앞의 요소가 min번 이상 포함된다는 것을 나타낸다. ex) a{1}   a가 1번이상 포함된 문자열
() 그룹핑 : 말 그대로 조건에 그룹을 표현하는 것.  ex) ( //d+001* )+
캡쳐 그룹 : 괄호 안의 조건 해당하는 글자를 골라내 따로 저장하는 용도 (regex_match 에서 씀)
| or 연산
\ 이스케이프 문자. ex) \. 은 '.' 이 임의의 한 문자를 나타내기 때문에 말그대로 마침표(.) 글자를 표현하기 위함
\\\a, \b\n, \f\r\t, \v.
백슬래시, 경고, 백스페이스, 줄 바꿈 , 양식 피드, 캐리지 리턴, 가로 탭, 세로 탭.
[ x - y ] ex) [a-c] a, b, c 중 매칭되는 것 하나

 

[예제]

"(100+1+ | 01)+" 라는 표현식 이 있다고 했을 때,

  • 100+ : 10 뒤에 0이 1개 이상 나오는 문자열
  • 1+ : 1이 1개 이상 나오는 문자열
  • 01 : "01" 문자열
  • | : 100+1+ 패턴이나 01 패턴 중 하나
  • () : 위 패턴이 하나 이상 반복되는 문자열

 

<3자리 국가코드, 4자리 중간번호, 4자리 마지막 번호> 로 표현 되는 핸트폰 번호 정규 표현식

"\\b010-\\d{4}-\\d{4}\\b"

  • \\b ~ \\b : 단어의 경계
  • 010- : "010-" 으로 시작되는 문자열
  • - : "-" 글자
  • \\d{4} : 4글자 숫자 인 문자열

"01[0-6]{1}-\\d{3,4}-\\d{4}"

  • 01 : "01" 로 시작되는 문자열
  • [0-6]{1} : 0~6 까지의 숫자 문자 1개.  ( {1} 생략 가능)
  • \\d{3,4} : 3글자 혹은 4글자의 숫자 문자열

 

정규 표현식 에서 쓰는 함수

Regular expressions library (since C++11) - cppreference.com

 

Regular expressions library (since C++11) - cppreference.com

Regular expressions library The regular expressions library provides a class that represents regular expressions, which are a kind of mini-language used to perform pattern matching within strings. Almost all operations with regexes can be characterized by

en.cppreference.com

미리보기

std::regex pattern("Get|GetValue");
std::cmatch m;
std::regex_search ("GetValue", m, re);    // returns true, and m[0] contains "Get"
std::regex_match ("GetValue", m, re);     // returns true, and m[0] contains "GetValue"
std::regex_search ("GetValues", m, re);  // returns true, and m[0] contains "Get"
std::regex_match ("GetValues", m, re);   // returns false

 

regex

정규 표현식 객체

가장 먼저 정규 표현식 객체를 정의해야 한다.

std::regex pattern("db-\\d*-log\\.txt");

 

정규 표현식 문법에는 여러 종류가 있다.

  • ECMAScript: JavaScript 및 .NET 언어에서 사용하는 문법과 가장 가깝습니다. (기본값)
  • basic: POSIX basic 정규식 또는 BRE입니다.
  • extended: POSIX extended 정규식 또는 ERE입니다.
  • awk: 이 extended경우 인쇄되지 않는 문자에 대한 이스케이프가 더 많이 있습니다.
  • grep: 이 basic경우 줄 바꿈(\n) 문자가 교대로 구분됩니다.
  • egrep: 이 extended경우 줄 바꿈 문자가 교대로 구분할 수도 있습니다.

그리고 여러 플래그를 적용할 수도 있다.

  • icase: 일치 시 대/소문자를 무시합니다.
  • nosubs: 표시된 일치 항목(즉, 괄호 안의 식)을 무시합니다. 대체 항목이 저장되지 않습니다.
  • optimize: 정규 표현식 객체를 생성하는데에는 시간이 좀 더 걸리지만 정규 표현식 객체를 사용하는 작업은 좀 더 빠르게 수행
  • collate: 로캘 구분 데이터 정렬 시퀀스(예: 양식 [a-z]범위)를 사용합니다.
  • (0개 이상의 플래그를 문법과 결합하여 정규식 엔진 동작을 지정할 수 있다)
std::regex pattern("db-\\d*-log\\.txt", std::regex::grep | std::regex::icase);

 

regex_match

문자열 매칭하기.

해당 문자열이 정규 표현식과 일치하는지 확인하는 함수. (bool 반환)

 

<"db-(숫자)-log.txt" 파일 이름 형식과 맞는지 확인하는 예제>

#include <iostream>
#include <regex>
#include <vector>

using namespace std;

int main() 
{
  // 확인할 파일 이름들.
  vector<string> file_names = {"db-123-log.txt", "db-124-log.txt",
                               "not-db-log.txt", "db-12-log.txt",
                               "db-12-log.jpg"};
                               
  regex pattern("db-\\d*-log\\.txt");
  
  for (const auto &file_name : file_names) 
  {
    // std::boolalpha 는 bool 을 0 과 1 대신에 false, true 로 표현하게 해줍니다.
    cout << file_name << ": " << std::boolalpha << regex_match(file_name, re) << endl;
  }
}

 

매칭되는 문자열 중, 부분 뽑아내기

정규 표현식과 일치하는 문자열 중에 일부분 추출하여 저장하는 방법

  • () 캡처 그룹을 사용하여 정규 표현식 중 원하는 부위에 감싼다.
  • smatch 타입 변수에 저장한다.

< 핸드폰 번호 중, 중간/마지막 번호 뽑아내는 예제>

vector<string> phone_numbers = { "010-1234-5678", "000-123-4567",
                                 "011-1234-5567", "010-12345-6789",
                                 "123-4567-8901", "010-1234-567" };
   
    regex pattern("01[0-6]{1}-(\\d{3,4})-(\\d{4})");
    smatch match;  // 매칭된 결과를 string 으로 보관
    
    for (const auto& number : phone_numbers) {
        if (regex_match(number, match, re)) {
            for (size_t i = 0; i < match.size(); i++) {
                cout << "Match : " << match[i].str() << endl;
            }
            cout << "-----------------------\n";
        }
    }
    
    // 그냥 match.str() 을 한다면 가장 처음 수집된 문자열이 반환된다.


 

regex_search

문자열 검색하기.

해당 문자열중에 정규 표현식과 일치하는 "일부" 문자열이 있는지 확인하는 함수. (bool 반환)

 

<문자열에서 숫자가 있는지 확인 및 뽑아내는 예제>

#include <iostream>
#include <string>
#include <regex>

std::string extractNumbers(const std::string& input) {
    std::regex pattern("\\d+");  // Regular expression to match one or more digits
    std::smatch match;
    std::string result;

    // Search the input string for numbers
    if (std::regex_search(input, match, re)) {
        result = match.str();  // Extract the first matched number
    }

    return result;
}

int main() {
    std::string input = "The price is 42 dollars";
    std::string numbers = extractNumbers(input);
    
    if (!numbers.empty()) {
        std::cout << "Found number: " << numbers << std::endl;
    } else {
        std::cout << "No numbers found." << std::endl;
    }

    return 0;
}

 

문자열 반복 검색하기.

regex_search() 는 반복 호출 시, 그냥 기존 문자열 기준으로 검색된 같은 패턴을 반복해서 반환한다.

검색된 패턴 이후 부터 다시 검색하는 방법은 match.suffix() 를 호출하여 반환된 문자열 기준으로 다시 검색하는 것이다.

  • match.suffix() 은 기존 문자열에서 검색된 패턴 바로 뒤 부터 끝까지 해당하는 ssub_match 객체를 리턴한다.
  • ssub_match 객체는 string 으로 캐스팅 변환하는 연산자가 들어있다.

 

<문자열에 존재하는 모든 숫자를 찾아내어 뽑아내는 예제>

std::vector<string> extractNumbers(const std::string& input) {
    std::regex pattern("\\d+");  // Regular expression to match one or more digits
    std::smatch match;
    std::vector<string> result;
    string check = input;
    // Search the input string for numbers

    while (std::regex_search(check, match, re)) {
        result.push_back(match.str());
        check = match.suffix();
    }

    return result;
}

int main() {
    std::string input = "The price is 42 or 75 dollars";
    std::vector<string> numbers = extractNumbers(input);

    if (!numbers.empty()) {
        for(string str : numbers)
        std::cout << "Found number: " << str << std::endl;
    }
    else {
        std::cout << "No numbers found." << std::endl;
    }

    return 0;
}

 

regex_iterator.

iterator 를 이용하면 좀 더 편리한 반복 검색을 수행할 수 있다.

  • sregex_iterator 단독 선언은 반복자의 종단(end) 를 의미한다.
std::vector<string> extractSecondNumber(const std::string& input) {
    std::regex pattern("\\d+");  // Regular expression to match one or more digits
    std::sregex_iterator currentMatch(input.begin(), input.end(), re);
    std::sregex_iterator lastMatch;

    std::vector<std::string> matches;

    while (currentMatch != lastMatch) {
        matches.push_back(currentMatch->str());
        ++currentMatch;
    }

   return matches;  // Return the second matched number
}

int main() {
    std::string input = "There are 42 apples and 23 oranges";
    std::vector<string> Numbers = extractSecondNumber(input);

    if (!Numbers.empty()) {
        for(string str : Numbers)
            std::cout << "Found number: " << str << std::endl;
    }
    else {
        std::cout << "numbers not founded." << std::endl;
    }

    return 0;
}

 

'유용한 기술' 카테고리의 다른 글

[pair의 확장] tuple  (0) 2024.05.26
[algorithm] transform & inserter  (0) 2024.05.14
(custom sort) Compare 작성  (0) 2024.05.11
C++ sscanf_s  (1) 2023.10.19
c++ algorithm [count, count_if]  (0) 2023.10.11

코딩테스트 연습 - 조이스틱 | 프로그래머스 스쿨 (programmers.co.kr)

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

문제 설명

  • 조이스틱으로 알파벳 이름을 완성하세요. 맨 처음엔 A로만 이루어져 있습니다.
    ex) 완성해야 하는 이름이 세 글자면 AAA, 네 글자면 AAAA
  • 조이스틱을 각 방향으로 움직이면 아래와 같습니다.
  • ▲ - 다음 알파벳
    ▼ - 이전 알파벳 (A에서 아래쪽으로 이동하면 Z로)
    ◀ - 커서를 왼쪽으로 이동 (첫 번째 위치에서 왼쪽으로 이동하면 마지막 문자에 커서)
    ▶ - 커서를 오른쪽으로 이동 (마지막 위치에서 오른쪽으로 이동하면 첫 번째 문자에 커서)
    
  • 예를 들어 아래의 방법으로 "JAZ"를 만들 수 있습니다.
  • - 첫 번째 위치에서 조이스틱을 위로 9번 조작하여 J를 완성합니다.
    - 조이스틱을 왼쪽으로 1번 조작하여 커서를 마지막 문자 위치로 이동시킵니다.
    - 마지막 위치에서 조이스틱을 아래로 1번 조작하여 Z를 완성합니다.
    따라서 11번 이동시켜 "JAZ"를 만들 수 있고, 이때가 최소 이동입니다.
    
  • 만들고자 하는 이름 name이 매개변수로 주어질 때, 이름에 대해 조이스틱 조작 횟수의 최솟값을 return 하도록 solution 함수를 만드세요.

 

제한 사항

  • name은 알파벳 대문자로만 이루어져 있습니다.
  • name의 길이는 1 이상 20 이하입니다.
  • 입출력 예namereturn
name return
"JEROEN" 56
"JAN" 23

 


 

매번 다시 풀 때마다 논리가 완벽히 바로 바로 세워지지가 않아 따로 정리함.

 

  • 각 요소마다 바꿔야될 알파벳으로 상하 움직임 갯수 체크는 for문 하나로 그냥 세면 됨.
  • 문제는 좌우로 최소한의 이동인 경우가 무엇이냐를 계산해야 한다.
    1. 첫 번째 위치부터 시작하여 앞으로 한 칸씩 이동하면서 체크 ( 0 <= i < size)
    2. i 번째 이후에, 변경할 필요가 있는 위치 p ('A' 가 아닌 곳) 를 찾는다. 
      • (앞으로 i번*2) + (뒤로 size-p) 번 이동하는 경우가 있고 
      • (앞으로 i번) + (뒤로 (size-p) *2) 번 이동하는 경우가 생긴다. 
      • 즉, [앞에 갔다 다시 뒤로 / 뒤로 갔다 다시 앞으로] 의 경우 중 최소값을 구해서 계속 갱신해 나가면 된다.
      • 처음 기준 값은 순행하는 경우인 size-1 번 이동한 수로 한다.
    3. 상하 최소값 + 좌우 최소값 더하면 답이다.
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

int LUT[] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,12,11,10,9,8,7,6,5,4,3,2,1 };

int solution(string name) {
    int answer = 0;
    for (auto ch : name)
        answer += LUT[ch - 'A'];	// 상하 최소값
    
    int len = name.length();
    int left_right = len - 1;
    for (int i = 0; i < len; ++i) 
    {
        
        int next_i = i + 1;
        while (next_i < len && name[next_i] == 'A')
            next_i++;
        
        left_right = min(left_right, i + len - next_i + min(i, len - next_i));
    }
    answer += left_right;
    return answer;
}

'코테 풀이 연습' 카테고리의 다른 글

행렬 테두리 회전하기  (0) 2024.05.20
빙고체크 c++  (0) 2023.02.25
C++ 유사 칸토어 비트열  (0) 2023.01.29

 

포인터끼리의 덧셈은 C++에서 허용되지 않는다.  포인터 연산 중에서 덧셈과 뺄셈은 다음과 같이 제한된다:

  • 포인터와 정수의 덧셈/뺄셈 : 이 연산은 포인터를 배열 내에서 이동시키기 위해 사용된다.
  • 포인터 간의 뺄셈 : 이 연산은 두 포인터 사이의 거리 차를 구하는데 사용된다. ( 또는 원점과 뺄셈하여 인덱스를 구할 수도 있다)
  • 증감 연산 : 포인터의 위치를 한 칸씩 옮기는 용도로 사용된다. ( 타입 단위로 주소값 이동 됨)

 

    int arr[3] = { 1,2,3 };
    
    // 포인터와 정수의 연산은 바이트 단위(정수 * sizeof(타입)) 으로 계산된다.
    // 포인터와 포인터 뺄셈 연산은 (타입)요소 단위로 계산된다.
    cout << ( &arr[0] + 1 ) << ", " << &arr[1] << "," << &arr[1] - &arr[0] << endl;

    // 컴파일에러. 배열의 요소 주소는 상수이며, 변경할 수 없다. (lvalue 여야함)
    int result = ++(&arr[1]);
    // 이렇게는 됨.
    int* ptr = &arr[0];
    ++ptr;
    
    //---------------------------------
    int* ptr1 = &arr[0];
    int* ptr2 = &arr[2];

    // 컴파일 에러. ( 포인터 끼리 덧셈 연산 x)
    int abc = ptr1 + ptr2;

 

'c, c++ 리마인드' 카테고리의 다른 글

라이브러리 import/export 키워드  (0) 2025.01.20
[c++] 참조자  (1) 2024.06.16
멤버 변수, 동적 할당 배열 크기 구하기  (0) 2024.04.12
[c++] typeid, is_base_of  (2) 2024.02.10
[c++] #define & typedef  (0) 2023.12.13

+ Recent posts