[ 문제 ] : https://programmers.co.kr/learn/courses/30/lessons/42577

 

코딩테스트 연습 - 전화번호 목록

전화번호부에 적힌 전화번호 중, 한 번호가 다른 번호의 접두어인 경우가 있는지 확인하려 합니다. 전화번호가 다음과 같을 경우, 구조대 전화번호는 영석이의 전화번호의 접두사입니다. 구조

programmers.co.kr


[ 문제 접근 ]

 

결국 못풀고 틀렸던 문제.

 

처음에는 phone_book을 unordered_set에 넣고, unordered_set에서 전화번호 길이 순서로 하나씩 꺼내서 나머지랑 비교하는 방법으로 푸려고 했으나, unordered_set 은 원소의 순서를 보장해주지 않으니, phone_book을 길이 순으로 정렬하고 앞에서부터 unordered_set 이랑 비교 하는 방법으로 접근.


[ 첫번째 풀이 ]

 

#include <string>
#include <vector>
#include <algorithm>
#include <set>
using namespace std;
/* # 1번째 풀이( 정확성 테스트 통과 / 효율성 테스트 실패)
- 문제 : [전화번호 목록] https://programmers.co.kr/learn/courses/30/lessons/42577
- 접근 방식 :
처음에는 phone_book을 unordered_set에 넣고,
unordered_set에서 전화번호 길이 순서로 하나씩 꺼내서 나머지랑 비교하는 방법으로 푸려고 했으나,
unordered_set 은 원소의 순서를 보장해주지 않으니,
phone_book vector를 문자열 길이 순서로 정렬하고 앞에서부터 unordered_set 이랑 비교 하는 방법으로 접근.
- 시간 복잡도 : O(n^2) ?
*/
bool cmp(const string& a,const string& b) {
return a.length() < b.length();
}
bool solution(vector<string> phone_book) {
//bool answer = true;
unordered_set<string> hash_set;
sort(phone_book.begin(), phone_book.end(), cmp);
for (auto& elem : phone_book) hash_set.insert(elem);
for (int i = 0; i < phone_book.size(); i++) {
int size = phone_book[i].length();
hash_set.erase(hash_set.find(phone_book[i])); // 비교할 문자를 hash_set에서 삭제.
// hash_set에 있는 나머지 문자들과 비교.
for (unordered_set<string>::iterator itr = hash_set.begin(); itr != hash_set.end();itr++) {
if(size > (*itr).length()) continue; // 시간 복잡도 줄이고자.
if (phone_book[i] == (*itr).substr(0, size)) return false;
}
}
return true;
}

 

[ 다른 사람 풀이 ]

#include <string>
#include <vector>
#include <algorithm>
using namespace std;
bool solution(vector<string> phone_book) {
sort(phone_book.begin(), phone_book.end());
for(int i = 0; i < phone_book.size(); ++i)
{
for(int j = i + 1; j < phone_book.size(); ++j)
{
if(phone_book[i] == phone_book[j].substr(0, phone_book[i].size()))
return false;
}
}
return true;
}

 

위는 다른 사람 블로그의 코드인데 이게 어떻게 통과되는지 모르겠다.

시간 복잡도도 자명하게 O(n^2) 아닌가 싶은데..

 

억울해서 실행해보니 그럼 그렇지!! 통과하지 못한다.

 

 

2021년 3월 4일에 테스트 케이스가 변경되었다는데 그전에 풀었던 사람인것 같다. 

 

근데 아래 처럼 이중 for문을 없애주면 통과가 된다.

 

[ 다른 사람 풀이 ]

#include <string>
#include <vector>
#include <algorithm>
using namespace std;
bool solution(vector<string> phone_book) {
bool answer = true;
sort(phone_book.begin(), phone_book.end());
for(int i=0; i<phone_book.size()-1; i++) {
//정렬을 먼저 했으므로 phone_book[i] < phone_book[i+1] 항상 크기 때문에 이중 포문 불필요
//string substr (size_t pos = 0, size_t len = npos)
if (phone_book[i] == phone_book[i+1].substr(0, phone_book[i].size()))
return false;
}
return answer;
}

 

그대로 난 hash 로 풀고 싶어서 다른 방법을 찾아봤다.

 

#include <string>
#include <vector>
#include <unordered_set>
#include <algorithm>
using namespace std;
bool solution(vector<string> phone_book) {
sort(phone_book.begin(), phone_book.end()); // 정렬
unordered_set<string> hash_map;
hash_map.insert(phone_book[0]); // 첫번째 문자열은 접두어 해시맵에 있는지 확인할게 없으니 저장만
for(int i = 1; i < phone_book.size(); ++i){ // 두번째 문자열부터 아래 과정!
// 1. 접두어가 있는지 확인
string str = "";
for(int j = 0; j < phone_book[i].length(); ++j){
str += phone_book[i][j]; // "abc" 라면 차례대로 "a", "ab", "abc"
if (hash_map.find(str) != hash_map.end()) // 해시맵에 존재한다면 false 리턴 후 종료
return false;
}
// 2. 내가 다른 단어의 접두어가 될 수도 있으니 나를 해시맵에 저장
hash_map.insert(phone_book[i]);
}
return true; // 한번도 리턴되지 못했다면 접두어 번호가 없는 것이다.
}

위와 같은 방법이 있었는데 랜덤한 

#include <string>
#include <vector>
#include <unordered_set>
#include <algorithm>
using namespace std;
bool cmp(const string& a,const string& b) { // 문자 길이가 긴거부터 내림차순.
return a.length() > b.length();
}
bool solution(vector<string> phone_book) {
//bool answer = true;
unordered_set<string> hash_set;
for (auto& elem : phone_book) hash_set.insert(elem);
sort(phone_book.begin(), phone_book.end(),cmp);
for (int i = 0; i < phone_book.size(); i++) {
string str = "";
// 중복 문자는 없으니 i번째 phone_book.size() -1 까지랑 비교하면 된다.
for(int j=0;j<phone_book[i].size()-1;j++){
str += phone_book[i][j];
if(hash_set.find(str) != hash_set.end()) return false;
}
}
return true;
}

 


[ Key Point ]

 

👉 substr 함수가 O(n)의 시간복잡도를 가진다는것을 알았다.

 

👉 unordered_set 의 erase 는 3가지 형태가 있는데,

(2) 번  size_type erase( const key_type& k) 는 erase 한 개수를 리턴하기 때문에 

itr = erase(key) 가 안된다.  ( 주의하자. 이것 때문에 시간 많이 썻다.)

by position (1)
iterator erase ( const_iterator position );
by key (2)
size_type erase ( const key_type& k ); // erase 한 개수 리턴.
range (3)
iterator erase ( const_iterator first, const_iterator last );

[ 다른 사람 풀이 ]

 

ref :: https://ansohxxn.github.io/programmers/kit2/

ref :: 

+ Recent posts