목차

0x00 정의와 성질

0x01 기능과 구현

0x02 STL deque

0x03 연습문제 

 


0x00 정의와 성질

 

덱은 양쪽 끝에서 삽입과 삭제가 모두 가능하다. 

deque == double linked queue 

 

추가, 제거, 앞/뒤 원소 확인 모두 O(1) 이다. 

 

그리고 덱 자료구조는 원소의 인덱스 확인이 애초에 불가능하다. 

하지만 독특하게도 STL deque에서는 인덱스로 원소 접근이 가능하다. (STL stack, queue에서는 인덱스로 원소 접근 불가.)


0x01 기능과 구현

 

배열로 간단히 덱을 구현해보자.

 

필요한 변수는 큐랑 똑같이 원소를 담을 큰 배열 한 개와 앞쪽, 뒤쪽을 가리킬 변수 두 개다.

head는 가장 앞에 있는 원소의 인덱스이고 tail을 가장 뒤에 있는 원소의 인덱스 + 1 이다. 

 

설령 앞의 스택과 큐는 직접 구현해보지 않았더라도, 덱은 꼭 짜봤으면 좋겠다. 

배열로 덱 구현 템플릿 링크 

 

 

 

 

0x02 STL deque

STL deque는 vector와 비슷한 느낌이 강하다. 

 

하지만, vector와 달리 deque는 모든 원소가 메모리 상에 연속적으로 배치되어 있지 않다. 

 

앞쪽과 뒤쪽의 추가/제거가 모두 필요하다면 deque를 쓰는게 효율적이다. 

그렇지 않다면 vector를 쓰면 된다. 

 

deque 레퍼런스

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

int main(void) {

    deque<int> deq;
    deq.push_front(10); // 10
    deq.push_back(50);  // 10 50
    deq.push_front(24); // 24 10 50

    for(auto x : deq) cout << x << ' ';
    cout << deq.size() << '\n';

    if(deq.empty()) cout << "deque is empty\n";
    else cout << "deque is not empty\n";
    deq.pop_front();  // 10 50
    deq.pop_back();   // 10

    cout << deq.back() << '\n';  // 10
    deq.push_back(72);        // 10 72
    cout << deq.front() << '\n'; // 10
    deq.push_back(12);        // 10 72 12
    deq[2] = 17;                 // 10 72 17

    deq.insert(deq.begin()+1, 33); // 10 33 72 17
    deq.insert(deq.begin()+4, 60); // 10 33 72 17 60

    for(auto x : deq) cout << x << ' ';
    cout << '\n';

    deq.erase(deq.begin()+3);
    cout << deq[3] << '\n';

    deq.clear(); // 모든 원소 제거
    cout << deq.size();
    return 0;
}

 

0x03 연습문제 

 

덱 문제집 

 

백준 문제 내 코드 
10866번 덱  덱 내코드
1021번 회전하는 큐  회전하는 큐 내코드
5430번 AC  

다음 시간에는 스택을 활용한 '수식의 괄호 쌍'을 배운다. 

공부 자료 출처: 바킹독 알고리즘 덱 

728x90

'알고리즘 > 실전 알고리즘' 카테고리의 다른 글

0x09강 - BFS  (0) 2021.12.07
0x08강 - 스택의 활용(수식의 괄호 쌍)  (0) 2021.11.29
0x06강 - 큐  (0) 2021.11.23
0x05강 - 스택  (0) 2021.11.23
0x04강 - 연결리스트  (0) 2021.11.22

목차

0x00 정의와 성질

0x01 기능과 구현

0x02 STL queue

0x03 연습문제 

 


0x00 정의와 성질

큐는 뒤에 원소를 넣고, 앞에서 원소를 빼는 자료구조다. 

큐 자료구조 방식은 넣은 순서대로 나오니깐, First In First Out 이라고 한다. FIFO

반대로 스택은 방금 넣은 것이 최상단에 있으니까 Last In First Out 이다. LIFO

 

스택과 똑같이 원소 추가/제거에 O(1) 시간이 걸린다. 

다른 점
큐는 뒤에서만 추가하고 앞에서만 제거한다.  => back에서 추가 front에서 제거 
스택은 top에서만 추가/제거한다. 

 

 

0x01 기능과 구현

배열을 이용해서 큐를 간단히 구현해보자. 

const int MX = 100005;
int dat[MX];
int head = 0, tail = 0;

데이터를 담을 배열을 선언한다.

데이터를 꺼낼 앞쪽 인덱스를 저장하는 head 와, 데이터를 넣을 뒤쪽 인덱스를 저장하는 tail을 선언한다. 

큐라는 자료구조와 STL queue에는 큐의 인덱스를 제공하지 않는다. 지금은 배열을 이용한 구현이기 때문에 인덱스를 이용한다. 

 

 

시작할 때는 tail 과 head가 모두 0 을 가리키고 있다. 

데이터를 넣고 빼고 하다보면, 자연스레 데이터가 뒤쪽 인덱스에 몰리게 된다. 

뒤에서 push 2번, 앞쪽에서 pop 1번

이를 해결하기위해 '원형 큐' 라는 개념을 쓴다. 

head나 tail이 마지막 인덱스(MX가 되면,) 7이 되면, 1이 더해질 때, 0번지를 쓰도록 하는 것이다. 

 

0x02 STLqueue

큐에서는 빼내는 앞쪽의 원소를 확인하는 front() 와 넣는 뒤쪽의 원소를 확인하는 back()이 있다. 

큐 레퍼런스 

// Authored by : std-freejia
#include <bits/stdc++.h>
using namespace std;

int main(void) {

  queue<int> qu;

  qu.push(10);  // 10
  qu.push(20);  // 10 20
  qu.push(30);  // (앞) 10 20 30 (뒤)

  cout << qu.size() << '\n';

  if(qu.empty()) cout << "Q is empty\n";
  else cout << "Q is not empty\n";

  qu.pop(); // 20 30
  cout << qu.front() << '\n';  // 20
  cout << qu.back() << '\n';   // 30
  qu.push(40); // 20 30 40
  qu.pop();       // 30 40

  return 0;
}

실행 결과 

3
Q is not empty
20
30

 

0x03 연습문제 

큐 문제집

문제번호 내 코드
10845번 큐 내 코드 
18258번 큐2 내 코드
2164번 카드2 내 코드

 


다음 시간에는 deque 덱을 배운다.

공부 자료 출처 : 바킹독 알고리즘 큐 

728x90

'알고리즘 > 실전 알고리즘' 카테고리의 다른 글

0x08강 - 스택의 활용(수식의 괄호 쌍)  (0) 2021.11.29
0x07강 - 덱  (0) 2021.11.24
0x05강 - 스택  (0) 2021.11.23
0x04강 - 연결리스트  (0) 2021.11.22
0x03강 - 배열  (0) 2021.11.21

목차

0x00 정의와 성질

0x01 기능과 구현

0x02 STL stack

0x03 연습문제 


0x00 정의와 성질

원소의 추가가 O(1)
원소의 제거가 O(1)
제일 상단의 원소 확인이 O(1)

 

stack은 상단에서만 데이터를 push pop 할 수 있는 자료구조다.

제일 상단이 아닌 나머지 원소들의 확인/변경이 원칙적으로 불가능하다! 

push() : 상단에 x를 추가 
pop()   : 최상단의 데이터를 제거 
top()   : 최상단의 데이터를 조회(확인)

0x01 기능과 구현

배열을 이용해 직접 스택을 구현해봤다.  기본 코드는 바킹독 github 를 가져왔고, 직접 push, pop, top 함수를 채웠다. 

 내가 작성한 코드 

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

const int MX = 1000005;
int dat[MX];
int pos = 0; // 삽입할 수 있는 인덱스

void push(int x){
    if(pos != MX){
        dat[pos] = x;
    }
    pos++;
}

void pop(){
    if(pos != 0) pos--;
}

int top(){
    return dat[pos-1];
}

void test(){
    push(5); push(4); push(3);
    cout << top() << '\n'; // 3
    pop(); pop();
    cout << top() << '\n'; // 5
    push(10); push(12);
    cout << top() << '\n'; // 12
    pop();
    cout << top() << '\n'; // 10
}

int main(void) {
    ios_base::sync_with_stdio(0);
    cin.ignore(0);

    test();
    return 0;
}

 

아래는 바킹독님이 제공한 정답 코드

내코드보다 짧게 처리된 부분이 많았다. 

void push(int x){
  dat[pos++] = x;
}

void pop(){
  pos--;
}

int top(){
  return dat[pos-1];
}

0x02 STL stack

STL에서 stack을 제공하니깐 코테에서는 이것을 이용하자. 

직접 stack을 구현하는 것 보다는, 실수를 줄일 수 있기 때문이다. 

스택이 비어있는데 top()을 호출하면 런타임 에러가 발생하니까 주의하자. 
스택이 비어있는데 pop()을 호출해도 마찬가지로 런타임 에러가 난다. 
#include <bits/stdc++.h>
using namespace std;

int main(void) {

    stack<int> s;
    
    s.push(10); // 10
    s.push(20); // 10 20
    s.push(30); // 10 20 30 (최근에 넣은 30이 꼭대기다.)
    cout << s.size() << '\n';  // 3

    if(s.empty()) cout << "S is empty\n";
    else cout << "S is not empty\n";   // S is not empty 출력

    s.pop(); // 10 20
    cout << s.top() << '\n';  // 20
    s.pop(); // 10
    cout << s.top() << '\n';  // 10
    s.pop(); // empty

    cout << s.top() << '\n'; // <--- runtime error 발생 !

    return 0;
}

 

0x03 연습문제 

스택 문제집 BOJ

백준 문제 내가 푼 코드
스택  
제로 내 코드
스택 수열  
내 코드
옥상 정원 꾸미기  
오큰수  
오아시스 재결합   

 


다음 시간에는 를 공부한다. 

공부 내용 출처  바킹독 알고리즘 스택

728x90

'알고리즘 > 실전 알고리즘' 카테고리의 다른 글

0x07강 - 덱  (0) 2021.11.24
0x06강 - 큐  (0) 2021.11.23
0x04강 - 연결리스트  (0) 2021.11.22
0x03강 - 배열  (0) 2021.11.21
0x02강 - 기초 코드 작성 요령2  (0) 2021.11.20

목차 

0x00  정의와 성질 

0x01 기능과 구현 

0x02 STL list

0x03 연습문제 


0x00  정의와 성질 

배열과 연결리스트 비교


연결리스트의 성질 

  • k번째 원소를 조회/변경하기 위해 O(k)가 필요함 
  • 임의의 위치에 원소를 추가/ 임의 위치의 원소를 제거 할때 O(1) 필요함
  • 배열과 달리 원소들이 메모리 상에 연속해있지 않아 Cache hit rate가 낮지만 할당이 다소 쉬움

연결리스트의 종류

  • 단일 연결 리스트 : 원소가 ‘다음’원소의 주소를 알고 있다.
  • 이중 연결 리스트 :  원소가 ‘이전’원소의 주소와 ‘다음’원소의 주소를 둘 다 알고 있다. 
  • 원형 연결 리스트 : 마지막 원소가 가장 처음원소의 주소를 알고 있다. 

배열과 연결리스트 비교

배열은 원소의 값만 저장한다. 반면에 연결리스트는 '다음 원소의 주소'도 저장하므로 메모리를 더 필요로 한다. 

연결리스트를의 시간복잡도를 정리하면, 

임의의 위치에 있는 원소를 확인하거나 변경 = O(N)
임의의 위치에 원소를 추가/ 임의 위치의 원소를 제거 = O(1)
(추가할 주소를 알고 있는 경우, O(1)에 가능하다는 의미다. )

배열에 비해, ‘다음 주소’만큼의 메모리를 소모한다. 

그래서 32비트 컴퓨터면 주소값이 32비트(=4바이트) 단위이니 4N 바이트가 추가로 필요하다.
64비트 컴퓨터라면 주소값이 64비트(=8바이트) 단위이니 8N 바이트가 추가로 필요하게 된다. 
즉 N에 비례하는 만큼의 메모리를 추가로 쓰게 된다. 


0x01 기능과 구현 

기능

임의의 위치에 있는 원소를 확인하거나 변경 = O(N)
임의의 위치에 원소를 추가/ 임의 위치의 원소를 제거 = O(1)

어디에 연결리스트가 어디에 활용될까 ?

메모장! 을 예로 들 수 있다.
커서를 옮기고, 글자를 지우고 이러한 변경 연산이 많다. 
이렇게 추가 제거 연산이 자주 발생하는 경우 연결리스트 사용을 고려해보자. 

구현

STL에서 제공하는 list를 쓰자. 
STL에 연결리스트가 있는데, 이 컨테이너의 이름은 list이고  내부적으로 Double Linked List로 구현되어 있다. 
면접에서의 손코딩을 대비할 때 STL list를 이용해서 끄적이며 연습하자. 

 

정석적인 구현

NODE 구조체나 클래스를 만들어서 원소가 할당될 때마다 동적할당을 하고, 노드를 삭제하면 메모리를 해제하는 식으로 구현한다. 

struct NODE{
  struct NODE *prev, *next;
  int data;
};

야매 연결 리스트

STL의 list가 제공되지 않는 경우에 임시로 쓰는 연결리스트 형태를 배운다 .
메모리 할당 및 해제를 생략하고 배열만을 이용한다. 
메모리 누수 문제 때문에 실무에서는 절대 쓸 수 없다. 
구현 난이도가 일반적인 연결리스트보다 낮고, 시간복잡도도 동일하므로 코테에서만 애용하면 된다. 

메모리 할당 및 해제를 생략한 형태

야매 연결리스트는 원소를 배열로 관리한다. 

이전/다음 원소 주소를 가리키는 대신, 배열 인덱스를 저장하는 방식으로 구현한 연결리스트다. 

dat 배열 : 데이터 저장 
pre 배열 : 이전 노드의 주소 저장
nxt 배열 : 다음 노드의 주소 저장  
unused: 새로운 원소가 들어갈 수 있는 인덱스. 원소가 추가되면 1씩 증가한다. 
0번지: 시작점을 나타내기 위한 dummy node. 실제로 값을 저장하지 않는다. 

pre 값이나 nxt 값이 -1이면 해당 원소의 이전/다음 원소가 존재하지 않는다는 의미다. 

모든 노드를 순회하여 출력하는 traverse() 함수 

0번지 (dummy node)에서 시작한다. 

더미노드가 가리키는 nxt는 2 인덱스를 가리킨다. 

따라서 더미노드 다음 노드는 2번지 노드다. 

void traverse(){
	int cur = nxt[0];
    while(cur != -1){
    	cout << dat[cur << ' ';
        cur = nxt[cur];
    }
    cout << '\n';
}



insert() 함수 직접 구현해보기 


erase() 함수 직접 구현해보기 

 

전체 코드 바킹독 github 

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

const int MX = 1000005;
int dat[MX], pre[MX], nxt[MX];
int unused = 1; // 사용가능한 인덱스 

void insert(int addr, int num){ // 삽입하려는 인덱스와 값을 파라미터로 받는다. 
  dat[unused] = num;   // 새로운 원소를 생성 
  pre[unused] = addr;  // 이전 노드의 다음 인덱스에 현재 삽입하려는 인덱스를 대입 
  nxt[unused] = nxt[addr]; // 새 원소의 nxt값에 삽입할 위치 nxt 값을 대입
  if(nxt[addr] != -1) pre[nxt[addr]] = unused; // 맨 마지막 원소에 데이터를 추가할 때를 처리.
  nxt[addr] = unused; // 삽입한 인덱스의 다음은 unused 
  unused++; // 새로 할당할 수 있는 인덱스 증가시키기 
}

void erase(int addr){
  nxt[pre[addr]] = nxt[addr]; // 이전 노드의 nxt에 삭제할 노드의 nxt할당. 
  if(nxt[addr] != -1) pre[nxt[addr]] = pre[addr]; // 삭제할 노드가 마지막이 아닌 경우 처리. 
}

 

왜 pre[addr]이 -1인지는 체크를 하지 않아도 되는데 nxt[addr]이 -1인지는 체크를 해야 할까?

더미노드가 존재하니까 pre[addr]는 항상 -1이 아니다. 
맨 마지막 원소일 경우 nxt[addr]가   -1일 수 있기 때문이다. 


0x02 STL list

vector와 비슷한 것이 많다. 

push_back, pop_back, push_front, pop_front는 모두 O(1)의 시간복잡도다. 


iterator가 주소 역할을 한다! 매우 유용함.

int main(void) {

    ios_base::sync_with_stdio(0);
    cin.ignore(0);

    list<int> L = {1, 2}; // 1, 2  
    list<int>::iterator t = L.begin(); // t는 1을 가리킨다. 
    
    L.push_front(10); // 10, 1, 2
    cout << *t << '\n'; // t는 1을 가리키니까, 1이 출력된다. 
    
    L.push_back(5); // 10, 1, 2, 5
    L.insert(t, 6); // t가 가리키는 곳 앞에 6을 삽입한다. 10, 6, 1, 2, 5
    t++; // t를 한 칸 뒤쪽으로 보낸다. t는 2를 가리킨다. 
    
    t = L.erase(t); // t가 가리키는 값을 제거하고, 그 다음 원소의 위치를 반환한다. 
    // 2를 제거하고 t는 5를 가리키게 된다. 10, 6, 1, 5
                    
    cout << *t << '\n'; // 5 
    
    // 순회하며 출력 
    for(auto i : L) cout << i << ' ';
    cout << '\n';
    
    for(list<int>::iterator it = L.begin(); it != L.end(); it++)
        cout << *it << ' ';
    
    return 0;

}

list::iterator라는 type을 치기가 귀찮으면 C++11 이상일 때 auto t = L.begin()으로 써도 된다. 


0x03 연습문제

연습문제를 풀어보고, 손코딩 문제는 잠시 생각해보고 답해보자. 

BOJ 1406번 에디터

BOJ 5397번 키로거 

BOJ 1158번 요세푸스 문제 

 

손코딩 문제 1 
원형 연결 리스트 내의 임의의 노드 하나가 주어졌을 때, 해당 리스트의 길이를 효율적으로 구하는 방법? 

 

정답
동일한 노드가 나올때 까지 계속 다음 노드로 가면 됨. 
공간복잡도 O(1)
시간복잡도 O(N)

손코딩문제 2 
중간에 만나는 두 연결 리스트의 시작점들이 주어졌을 때, 만나는 지점을 구하는 방법은? 

 

정답 
일단 각 리스트의 길이를 구한다. 
더 긴 리스트에서 둘의 길이 차이 만큼 앞으로 이동시켜 놓는다. 
두 시작점이 만날 때 까지 두 시작점을 동시에 한 칸씩 전진 시키면 된다. 

 

손코딩문제 3 
주어진 연결 리스트 안에 사이클이 있는지 판단하라. 

 

정답 
Floyd’s cycle-finding algorithm, 공간복잡도 O(1), 시간복잡도 O(N)
한 칸씩 가는 커서와 두 칸씩 가는 커서를 동일한 시작점에서 출발시킨다. 
사이클이 없으면, 두 커서가 만나지 못하고 연결리스트의 끝에 도달한다. 
사이클이 있으면, 두 커서는 반드시 만나게 된다. 
거치는 모든 노드를 저장할 필요가 없다.  


다음 시간에는 스택을 공부한다. 

공부 내용 출처 : 바킹독 알고리즘 연결 리스트  

728x90

'알고리즘 > 실전 알고리즘' 카테고리의 다른 글

0x06강 - 큐  (0) 2021.11.23
0x05강 - 스택  (0) 2021.11.23
0x03강 - 배열  (0) 2021.11.21
0x02강 - 기초 코드 작성 요령2  (0) 2021.11.20
0x01강 - 기초 코드 작성 요령1  (0) 2021.11.19

문제

키로거 백준 5397

"맞았습니다"코드

#include <bits/stdc++.h>
using namespace std;
​
int tc; // 테스트케이스
string inputStr; // 입력받은 키로거 문자열 
list<char> L;
list<char> ::iterator cur;
​
void input(){
​
    cin >> tc; // 테스트케이스 횟수
​
    while(tc--){
        
        cin >> inputStr; // 문자열 입력받기
​
        L.clear(); // list 초기화
​
        cur = L.begin(); // 빈 리스트의 시작점에 커서를 둔다.
​
        for(int i = 0; i < inputStr.length(); i++){
​
            if(inputStr[i] == '<'){ // 커서를 왼쪽으로
                if(cur != L.begin()){
                    cur--;
                }
            }
            else if(inputStr[i] == '>'){ // 커서를 오른쪽으로 
                if(cur != L.end()){
                    cur++;
                }
            }
            else if(inputStr[i] == '-'){ // 백스페이스
                if(cur != L.begin()){
                    cur = L.erase(--cur); // 커서를 앞으로 당긴 후, 삭제.
                }
            }
            else {
                L.insert(cur, inputStr[i]);
            }
        } // for
​
        // 출력
        for(cur = L.begin(); cur != L.end(); cur++){
            cout << *cur;
        }
        cout << '\n';
​
    } // while
}
​
int main(void) {
    ios_base::sync_with_stdio(0);
    cin.ignore(0);
​
    input();
    return 0;
}

리뷰

키로거 문자열을 입력받고, 문자열을 순회하면서 list에 문자을 삽입한다.

커서도 list에서 움직이게 했다. 빈 리스트의 시작점에 커서를 뒀다.

백스페이스 처리할 때, 커서를 앞으로 당긴 후에 erase()를 해야 되는 것을 유념하자.

728x90

문제

에디터 1406

List 를 이용한 "맞았습니다"코드

#include <bits/stdc++.h>
using namespace std;
​
list<char> strList;
string str;
int M;
char ch, x;
​
void input(){
​
    cin >> str >> M; // 초기 문자열, 명령의 횟수
​
    // 문자열의 문자를 리스트에 넣는다.
    for(int i = 0; i < str.size(); i++){
        strList.push_back(str[i]);
    }
​
    auto cur = strList.end(); // 커서는 마지막을 가리키고 있다
​
    while(M--){ // 명령 수행
​
        cin >> ch;
​
        if(ch == 'P') {
            cin >> x;
            strList.insert(cur, x); // 문자를 커서 왼쪽에 추가
        }
        else if(ch == 'L') {
            if (cur != strList.begin()) { // 맨 처음이 아니면,
                cur--; // 커서를 왼쪽으로 한 칸 옮김
            }
        }
        else if(ch == 'D'){
            if(cur != strList.end()){ // 커서가 맨 마지막이 아니면,
                cur++; // 커서를 오른쪽으로 한 칸 옮김
            }
        }else { // 'B'
            if(cur != strList.begin()){ // 커서가 맨 마지막이 아니면,
                cur--; // 커서 기준 왼쪽이니까 -- 해준다.
                cur = strList.erase(cur); // 문자 삭제 후, 주소값을 갱신
            }
        }
    }// while
​
    for(auto c : strList) cout << c;
}
​
int main(void) {
    ios_base::sync_with_stdio(0);
    cin.ignore(0);
​
    input();
    return 0;
}

Stack 을 이용한 "맞았습니다"코드

#include <bits/stdc++.h>
using namespace std;
​
stack<char> leftStack;
stack<char> rightStack;
string str;
int M;
char ch, x;
​
void input(){
​
    cin >> str >> M;
​
    for(int i = 0; i < str.size(); i++){
        leftStack.push(str[i]);
    }
​
    while(M--){ // 명령 횟수만큼 반복 
​
        cin >> ch; // 명령 문자 
​
        switch(ch){
            case 'P':
                cin >> x; // 추가할 문자 
                leftStack.push(x); // 문자를 커서 왼쪽에 추가
                break;
            case 'L':
                if(!leftStack.empty()){ // 왼쪽 스택에 문자가 있는지 확인 
                    rightStack.push(leftStack.top());  // 왼쪽 스택에 있던 top을 오른쪽 스택에 넣는다 
                    leftStack.pop(); // 실제 pop()연산 수행 
                }
                break;
            case 'D':
                if(!rightStack.empty()){ // 커서 오른쪽에 문자가 있는지 확인 
                    leftStack.push(rightStack.top());
                    rightStack.pop();
                }
                break;
            case 'B':
                if(!leftStack.empty()){
                    leftStack.pop();
                }
                break;
        }// switch
    }// while
​
    while(!leftStack.empty()){ // 문자를 순서대로 꺼내야 하니까. 왼쪽스택에 있던것을 오른쪽으로 전부 이동시킴
        rightStack.push(leftStack.top());
        leftStack.pop();
    }
​
    while(!rightStack.empty()){ // 실제 답 출력 
        cout << rightStack.top();
        rightStack.pop();
    }
}
​
int main(void) {
    ios_base::sync_with_stdio(0);
    cin.ignore(0);
​
    input();
    return 0;
}


연결리스트를 이용한 경우 리뷰

커서의 위치를 저장하기 위해 iterator를 이용했다.

auto cur = strList.end(); // 커서는 마지막을 가리키고 있다

erase 함수로 커서가 가리키는 문자를 삭제하고 나서, 커서의 주소를 갱신해주는 것을 유의해야 한다.

cur = strList.erase(cur); // 문자 삭제 후, 주소값을 갱신

스택을 이용한 경우 리뷰

커서를 기준으로 두고 스택을 2개 사용했다.

커서를 기준으로 왼쪽에 저장할 문자를 담을 스택, 커서를 기준으로 오른쪽에 저장할 문자를 담을 스택.

4가지 연산 모두 커서 위치를 기준으로 발생하기 때문이다.

728x90

'알고리즘 > 백준' 카테고리의 다른 글

요세푸스 문제 list, queue로 풀어본 백준 1158번 c++  (0) 2021.11.24
키로거 백준 5397 c++  (0) 2021.11.22
strfry 백준 11328 c++  (0) 2021.11.22
두 수의 합 백준 3273번 c++  (0) 2021.11.22
방 번호 백준 1475 c++  (0) 2021.11.21

문제

strfry 백준 11328 c++

"맞았습니다."코드

#include <bits/stdc++.h>
using namespace std;
​
int T;
​
void func(string a, string b){
​
    int cntA[35] = {};
    int cntB[35] = {};
​
    for(int i = 0; i < a.length(); i++){
        cntA[a[i]-97]++;
    }
    for(int i = 0; i < b.length(); i++){
        cntB[b[i]-97]++;
    }
​
    string answer = "Possible\n";
    for(int i = 0; i < 79-61+1; i++){
        if(cntA[i] != cntB[i]){
            answer = "Impossible\n";
            break;
        }
    }
​
    cout << answer;
}
​
void input(){
    cin >> T;
    string a, b;
    for(int i = 0; i < T; i++){
        cin >> a >> b;
        func(a, b);
    }
}
​
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
​
    input();
    return 0;
}


리뷰

소문자 알파벳 개수가 동일한지 비교하면 된다.

소문자 알파벳과 ascii 코드를 매칭하기 위해 문자열에서 97을 뺐다.

 

소문자 a에서 97을 빼면 0이 나온다.

소문자 a부터 소문자 z까지 아스키코드는 연속적이니까 이렇게 인덱스로 매칭 할 수 있다.

단어에서 a의 개수를 배열의 0인덱스의 값에 저장했다. b의 개수는 배열의 1인덱스 값에 저장했다.

 

728x90

+ Recent posts