Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[맵과 셋] 3월 8일 #2

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions [맵과 셋] 3월 8일/11478_sample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <iostream>
#include <set>

using namespace std;

// 서로 다른 부분 문자열 구하는 함수
int cntDiff(string s) {
set<string> sub; // 중복 없이 부분 문자열 저장
// 문자열의 길이만큼 반복
for (int i = 0; i < s.length(); i++) { // 문자열 시작 부분
string temp = ""; // 빈 문자열 temp 선언
for (int j = i; j < s.length(); j++) { // 문자열 끝 부분
temp += s[j]; // i-j 부분 문자열
sub.insert(temp); // 삽입
}
}
return sub.size(); // 부분 문자열 개수 return
}

/**
* 중복을 제거해서 저장해주는 컨테이너인 set을 활용해서 풀이 가능
* 삽입만 하므로 트리 구조 셋(set)이나 해시 구조 셋(unordered_set) 중 무엇을 쓰든 크게 상관 X
*/

int main() {
string s; // 문자열 s 선언

// 입력
cin >> s; // 입력 받은 문자열 저장
// 연산 & 출력
cout << cntDiff(s); // cntDiff(s)함수 호출
return 0;
}
41 changes: 41 additions & 0 deletions [맵과 셋] 3월 8일/19583_sample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <iostream>
#include <map>

using namespace std;

/**
* 개강총회가 시작하기 전에 입장한 학회원을 먼저 map에 삽입
* 개강총회를 끝내고 나서부터 스트리밍을 끝낼 때까지의 시간대의 채팅 기록을 보고 퇴장 여부 확인
* -> 이때, 한 사람이 채팅 여러 개 남겼을 수 있으므로 이미 확인한 사람 체크하는 것이 중요 (map의 value 값 활용해서 체크)
* 시간은 문자열로 받아서 처리 (대소관계 숫자와 동일)
*/

int main() {
// 로컬에서 편하게 확인하기 위해 파일로 입력을 받아서 사용!
// freopen("input.txt", "r", stdin);

// 입출력 처리 속도 향상
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);

string s, e, q, t, name; // 문자열 s, e, q, t, name 선언
map<string, bool> m; // 맵 m 선언
int ans = 0; // 출석 확인된 학회원 인원 수

// 입력 & 연산
cin >> s >> e >> q; // s, e, q 입력 받음
while (cin >> t >> name) { // 입력이 있는 동안 반복
if (t <= s) { // 개강총회 시작하기 전 입장 시간대
m[name] = true; // 우선 저장
} else if (t >= e && t <= q) { // 개강총회 끝 ~ 스트리밍 끝 시간대
if (m[name]) { // 출석 확인
ans++; // 출석 확인 학회원 수 증가
m[name] = false; // 같은 사람 여러 번 출석 되면 안되므로 체크
}
}
}
// 출력
cout << ans << '\n';
return 0;
}
42 changes: 42 additions & 0 deletions [맵과 셋] 3월 8일/2776.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// Created by Subeen on 3/14/2022.
//
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);

int t, n, m, input;
vector<int> v1, v2;

cin >> t;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 코드가 틀렸습니다! 라고 나오는 일차적인 이유는 전체과정을 t만큼 반복해주는 부분 코드를 작성하지 않았기 때문이에요! 아래 입출력 예시 참고하셔서 한줄만 추가하시면 아마 틀렸습니다! 를 받으시진 않을거에요! 그런데 사실 이문제는 이부분을 고쳐도 맞았습니다! 를 받지 못합니다😢 시간초과가 발생했기 때문인데요! 그 이유는 아래 커멘트 남겨둘테니 참고해주세요!

[입력]

2 //두번 반복
5
4 1 5 2 3
5
1 3 7 9 5
5
4 1 5 2 3
5
1 3 7 9 5

[출력]

1
1
0
0
1
1
1
0
0
1

cin >> n;
// vector v1에 수첩1 정수 저장
for (int i = 0; i < n; i++) {
cin >> input;
v1.push_back(input);
}

cin >> m;
// vector v2에 수첩2 정수 저장
for (int i = 0; i < m; i++) {
cin >> input;
v2.push_back(input);
}
// 수첩2의 정수가 수첩1에 있는지 확인 (수첩2 정수의 순서대로, 수첩1에 있으면 1, 없으면 0)
for (int i = 0; i < m; i++) {
// 수첩1에 수첩2의 정수가 없을 때 -> 0
if (find(v1.begin(), v1.end(), v2[i]) == v1.end()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

find함수를 활용해서 수첩1 벡터에서 수첩2의 숫자가 있는지 찾는 방식 식으로 구현방식을 떠올려주신것은 정말 좋아요! 그런데 사실 벡터를 활용하면 시간초과가 발생해서 문제를 해결할 수 없어요!

만약 N의 범위가 작았더라면 작성해주신 방식으로도 문제를 해결할 수 있었을거에요! 그런데 본 문제는 1 ≤ N ≤ 1,000,000 이므로 N이 굉장히 큽니다. 그런데 vector 컨테이너를 사용하게 된다면, 탐색에 소요되는 시간은 O(N)이에요. 모든 원소를 다 뒤져야하니까요! 따라서 수첩2에 존재하는 모든 단어에 대해 수첩1에 있는지 확인해야하니까 이중포문을 돌아야하죠. O(N^2) =1,000,000 * 1,000,000 >>> 2억 이므로 당연히 시간초과가 날거에요! 따라서 숫자찾기를 최대한 빠른 시간 내에 마쳐야합니다!

이를 위해 빠른 탐색을 위한알고리즘 혹은 자료구조를 사용해볼 수 있어요. 방법이 단 한가지만 있지는 않아요! 우선적으로는, algorithm 헤더에 존재하는 시간복잡도가 O(logN)인 이진탐색(binary_search) 을 이용해볼 수도 있구요! map(set) 보다 탐색이 빠른 unordered_map(unordered_set) 을 이용해보실 수도 있어요! 해시 맵의 원리를 이용하기 때문에 key가 유사한 데이터가 많은 것이 아니라면 map(set)보다 더 빠른 탐색을 하기 위한 자료구조로서 유용하게 쓰일 수 있죠! unordered_map(unordered_set)은 해쉬테이블로 구현한 자료구조로 탐색 시간복잡도는 O(1)이에요! Hash 아키텍쳐를 이용하면 탐색, 삽입 , 삭제 모두 O(1)의 시간복잡도를 갖습니다. 혹시 궁금하실까봐 unordered_map 활용법 관련한 링크도 같이 첨부할게요!

https://blog.naver.com/PostView.nhn?blogId=webserver3315&logNo=221678909965&parentCategoryNo=&categoryNo=48&viewDate=&isShowPopularPosts=true&from=search

문제를 푸시다가 "그냥 map 도 이진 트리 구조로서 탐색에 O(logN)의 시간복잡도를 가지지 않나요? binary_search와 시간복잡도가 같은데 왜 map은 시간초과가 나고 binary_search 는 시간초과가 나지 않는지 모르겠어요!" 하고 질문하실 수도 있어요! 그런데 이진트리, 정확히는 RB tree라고 해도 binary search보단 연산이 많아요. 트리 구조 유지를 위한 회전 등, 한번 정렬해두면 끝인 binary search에 비해 계속 삽입과정을 반복하게 되어 map은 시간초과가 발생합니다. 아마 map이 직접 구현한 binary search보다 3~4배정도 느린것 같아요. 따라서 n이 100만일때 O(nlogN)이라면 아슬아슬할거에요. 그래서 탐색을 할때에는 트리를 만들기 보다는 단순하게 이진탐색을 활용해주시는 방법이 훨씬 효율적이고 오버헤드도 적어요! set도 map과 같은 이진트리 구조니 비슷한 결과가 나오겠죠!
+제가 직접해보니 서버상황에따라 set, map으로는 통과할때도 있고 안될때도 있는 것 같아요!

cout << 0 << '\n';
} else { // 수첩1에 수첩2의 정수가 있을 때 -> 1
cout << 1 << '\n';
}
}
return 0;
}
45 changes: 45 additions & 0 deletions [맵과 셋] 3월 8일/2776_sample1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <iostream>
#include <unordered_set>
using namespace std;

/**
* set을 이용한 풀이
* 정렬을 할 필요가 없이, 삽입과 검색만 일어나는 문제
* 입력의 수가 최대 1,000,000으로 삽입과 탐색이 많이 일어남
* 따라서, O(log N)의 set이 아니라 O(1)인 unordered_set을 사용해서 풀이
*/

int main() {
// 입출력 처리 속도 향상을 위한 코드
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);

// 입력
int t; // 정수형 변수 t 선언
cin >> t; // t에 입력값 저장
while(t--) {
int n, m, input; // 정수형 변수 n, m, input 선언 (n: 수첩1 정수 개수, m: 수첩2 정수 개수, input: 수첩의 정수)
unordered_set<int> note1; // 정렬되지 않은 set note1 선언

// 수첩1에 해당되는 원소들을 unordered_set에 삽입
cin >> n; // n에 입력값을 받음
while (n--) { // 반복해서 수첩에 추가
cin >> input; // input에 입력값을 받음
note1.insert(input); // note1에 input 값 추가
}
cin >> m; // m에 입력값을 받음
while (m--) { // 반복해서 수첩에 추가
cin >> input; // input에 입력값을 받음
// 반복자를 이용해서 원소가 셋에 포함되어 있는지 확인
auto iter = note1.find(input);
// 수첩1에 없이면 0, 있으면 1 출력
if (iter == note1.end()) {
cout << "0\n";
} else {
cout << "1\n";
}
}
}
return 0;
}
49 changes: 49 additions & 0 deletions [맵과 셋] 3월 8일/2776_sample2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

/**
* 이분탐색을 이용한 풀이
* O(n)의 시간복잡도를 갖는 선형탐색과 달리, 이분탐색은 O(logN)의 시간복잡도를 가짐
*/

int main() {
// 입출력 처리 속도 향상
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);

// 입력
int t; // 정수형 변수 t 선언
cin >> t; // t에 입력값 저장
// 반복
while (t--) {
int n, m, input; // 정수형 변수 n, m, input 선언 (n: 수첩1 정수 개수, m: 수첩2 정수 개수, input: 수첩의 정수)

// 수첩1에 해당되는 원소들을 vector에 저장
cin >> n; // n에 입력값 저장
vector<int> note1(n, 0); // vector note1 생성
// 반복문을 이용해서 수첩1에 정수 저장
for (int i = 0; i < n; i++) {
cin >> note1[i];
}
// 이분탐색을 하기 위해 정렬
sort(note1.begin(), note1.end());

cin >> m; // m에 입력값 저장
while (m--) {
cin >> input; // input에 입력값 저장
// 이분탐색 라이브러리 함수를 활용해, 원소가 수첩1에 엤는지 확인
// binary_search() -> 이분탐색으로 원소가 있는지를 확인하고 결과를 리틴하는 함수
// 수첩1에 없이면 0, 있으면 1 출력
if (binary_search(note1.begin(), note1.end(), input)) {
cout << "1\n";
} else {
cout << "0\n";
}
}
}
return 0;
}