[중급 알고리즘] 스택

2022. 6. 24. 02:24TIL💡/Algorithms

LIFO의 특성을 활용

 

💚 9935 문자열 폭발

- 문자열 S에서 폭발 문자열 T를 모두 지우는 문제

- 스택에 넣는 것은 (문자열의 인덱스, 폭발 문자열에서 인덱스)

- 현재 문자가 폭발 문자열의 첫 번째 문자와 같으면 스택에 넣는다.

- 다른 경우에 스택에 비어있으면 그냥 넘어간다.

- 다른 경우에 스택이 비어있지 않으면 스택의 가장 위에 있는 것의 폭발 문자열의 인덱스를 가져온다. 이 인덱스를 p라고 한다. 

- 현재 문자가 폭발 문자열의 p + 1와 같으면 스택에 넣는다. (p + 1이 폭발 문자열의 마지막 문자면, 폭발 문자열을 찾은 것이다. 스택에서 폭발 문자열을 지운다.)

- 다르면 스택을 모두 비워버린다.

- 폭발 문자열의 길이가 1이면 스택을 사용할 수 없기 때문에 그냥 for문을 돈다.

 

T가 모두 다른 문자로 구성되어있는 문자열이라는 점에 가능한 풀이다.

 

 strlen :문자열의 길이 반환

 

#include <stack>
#include <cstring>
#include <iostream>

using namespace std;
char a[1000001];
char b[100];
bool erased[1000001];
int main() {
    cin >> a;
    cin >> b;
    int n = strlen(a);
    int m = strlen(b);
    if(m == 1) {
        for(int i = 0;i < n;i++) {
            if(a[i] == b[0]) {
                erased[i] = true;
            }
        }
    }
    else {
        // a의 인덱스, b의 인덱스
        stack<pair<int,int>> s;
        for(int i = 0;i < n;i++) {
            if(a[i] == b[0]) {
                s.push(make_pair(i, 0));
            }
            else {
                if(!s.empty()) {
                    auto p = s.top();
                    // 이전에 삽입된 문자
                    if(a[i] == b[p.second + 1]) {
                        s.push(make_pair(i, p.second + 1));
                        if(p.second + 1 == m - 1) {
                            for(int k = 0;k < m;k++) {
                                auto p = s.top();
                                erased[p.first] = true;
                                s.pop();
                            }
                        }
                    }
                    else {
                        while(!s.empty()) {
                            s.pop();
                        }
                    }
                }
            }
        }
    }
    
    bool printed = false;
    for (int i = 0;i < n;i++) {
        if(erased[i]) continue;
        cout << a[i];
        printed  = true;
    }
    if(!printed) {
        cout << "FRULA\n";
    }
    else {
        cout << '\n';
    }
    
    return 0;
}

💚 6549 히스토그램에서 가장 큰 직사각형 💥

높이는 항상 막대의 높이 중 하나와 일치할 수밖에 없다.

그래서 각각의 막대에 대해 만들 수 있는 가장 큰 직사각형을 찾으려고 한다.

 

모든 막대 x에 대해서, x를 높이로 하면서 만들 수 있는 가장 큰 직사각형을 찾아야 한다.

x를 높이로 하면서 만들 수 있는 가장 큰 직사각형은 x의 왼쪽에 있는 막대 중에 x보다 작은 첫 번째 막대 left와

x의 오른쪽에 있는 막대 중에서 x보다 높이가 작은 첫 번째 막대 right를 찾아야 한다.

 

왜 스택을 이용하는가?

직사각형의 높이가 비연속적이라는 점을 활용하기 위함이다.

스택에 직사각형의 높이를 추가할 때 스택 안에 있는 직사각형까지는 갈 수 있다라는 점을 전제한다.

중간에 막히는 직사각형은 이미 pop되었다는 점을 활용한다.

 

스택을 사용해 모든 막대를 스택에 한번씩만 삽입하므로 시간복잡도는 $O(N)$이다.

#include <iostream>
#include <stack>

using namespace std;
long long a[100000];
int main() {
    while(true) {
        int n;
        cin >> n;
        if(n == 0) break;
        for(int i = 0;i < n;i++) {
            cin >> a[i];
        }
        
        stack<long long> s;
        long long ans = 0;
        for(int i = 0;i < n;i++) {
            while(!s.empty() && a[s.top()] > a[i]) {
                long long height = a[s.top()];
                s.pop();
                long long width = i;
                // width 계산 방법
                if(!s.empty()) {
                    width = (i - s.top() - 1);
                }
                if(ans < width * height) {
                    ans = width * height;
                }
            }
            s.push(i);
        }
        
        while(!s.empty()) {
            long long height = a[s.top()];
            s.pop();
            long long width = n;
            if(!s.empty()) {
                width = n - s.top() - 1;
            }
            
            if(ans < width * height) {
                ans = width * height;
            }
        }
        
        cout << ans << '\n';
    }
    return 0;
}

💚 3015 오아시스 재결합

위 문제와 유사한 문제이다.

 

예시로 17452386

1은 앞에 뭐 없으니 push

 

7은 앞의 1보다 크므로 1은 7을 볼 수 있다. → 정답에 1을 더해주고

그 뒤의 것들은 1을 앞으로 절대로 볼 수 없다. 즉 스택에서 없어도 좋다. → 1을 스택에서 제거하고 7을 스택에 추가

for(int i = 0;i < n;i++) {
	while(!s.empty()) {
    	if(s.top() < a[i]) {
        	ans += 1;
            s.pop();
        }
        else {
        	break;
        }
    }
    if(!s.empty()) ans += 1;
    s.push(a[i]);
}

 

그런데 여기서 키가 같은 경우도 고려를 해야한다. 스택에 키를 가진 사람의 수도 넣어서 풀어야 한다.

#include <iostream>
#include <stack>

using namespace std;
int a[500000];
int n;
int main() {
    cin >> n;
    for(int i = 0;i < n;i++) {
        cin >> a[i];
    }
    // 키
    stack<int> s;
    // 동일한 키인 사람 수
    stack<int> c;
    long long ans = 0;
    for(int i = 0;i < n;i++) {
        int h = a[i];
        int cnt = 1;
        while(!s.empty()) {
            // 어차피 a[i] 보다 작은 키는 앞으로 안 보이기 때문
            if(s.top() <= a[i]) {
                ans += (long long) c.top();
                // 연속적으로 키가 동일하면 인원 누적
                if(s.top() == a[i]) {
                    cnt += c.top();
                }
                s.pop();
                c.pop();
            }
            else {
                break;
            }
        }
        // 어차피 a[i]와 동일한 높이는 이미 pop됐기 때문
        if(!s.empty()) {
            ans += 1LL;
        }
        s.push(h);
        c.push(cnt);
    
    }
    cout << ans << '\n';
    return 0;
}

 

 

 

 

'TIL💡 > Algorithms' 카테고리의 다른 글

[AtCoder] Batters  (0) 2022.06.30
[백준] 2240: 자두나무  (0) 2022.06.30
[중급 알고리즘] Brute Force 확장3  (0) 2022.06.19
[중급 알고리즘] Brute Froce 확장 2  (0) 2022.06.18
[Codeforces] 1694B. Paranoid String  (0) 2022.06.17