#include <bits/stdc++.h>
using namespace std;
int n;
string st;
vector<string> v;
bool cmp(string &a, string & b){
if(a.size() != b.size()) return a.size() < b.size(); // 길이 짧은것 우선
else return a < b; // 사전순
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for(int i = 0; i < n; i++) {
cin >> st;
v.push_back(st);
}
sort(v.begin(), v.end(), cmp);
v.erase(unique(v.begin(), v.end()), v.end());
for(auto i : v) cout << i << '\n';
return 0;
}
리뷰
중복을 제거하는 것은 unique와 erase를 썼는데 이번에 풀면서 배웠다. unique 를 쓰면 중복은 제거되도 벡터의 크기가 줄어들지는 않는다. unique는 중복되는 원소의 포인터를 맨 뒤로 보내기 때문이다. unique의 리턴 값은 중복 제거 된 원소의 포인터다. 그래서 erase로 중복 제거된 원소의 포인터 부터 맨 마지막 포인터까지 공간을 삭제해준다. erase를 하면 아예 메모리가 해제되니까 중복된 부분의 크기만큼 반환되는 것이다.
#include <bits/stdc++.h>
using namespace std;
int n, num;
int arr[2000002];
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> num;
arr[num+1000000]++;
}
for(int i = 0; i <= 2000000; i++) {
while(arr[i] > 0) {
cout << i-1000000 << '\n';
arr[i]--;
}
}
return 0;
}
리뷰
절대값이 100만 이하인 정수가 입력으로 들어오니까. 양수 음수 처리를 따로 해야겠다고 생각했었다. 일단 정답 코드를 제출하고 다른 분의 코드를 봤다. 200만 크기의 배열을 쓰면서도 어차피 입력받은 대로 더하고 빼주고 하면 입력 받은 숫자를 출력할 수 있으니까. 입력받을 때 저장하는 인덱스 : num + 1000000 출력할 때 숫자 : num - 1000000 이렇게 하면 충분했다.
두 원소의 우선순위가 같다면 원소의 위치를 변경하지 않는다. 사용법은 sort()와 똑같다.
[중요] 비교 함수
sort() 함수의 파라미터로 비교함수를 넘길 수 있다. stable_sort()도 마찬가지다.
예를 들어, int형을 5로 나눈 나머지 순으로, 5로 나눈 나머지가 같다면 크기 순으로 정렬하고 싶으면 아래 코드처럼 하면 된다.
비교함수 cmp는 a가 b의 앞에 와야할 때 true를, 그렇지 않을 때 false를 반환한다.
bool cmp(int a, int b){ // a가 b의 앞에 와야할 때 true, 아니면 false
if(a % 5 != b % 5) return a % 5 < b % 5;
else a < b;
}
int a[7] = {1, 2, 3, 4, 5, 6, 7};
sort(a, a+7, cmp);
// 출력 결과: 5 1 6 2 7 3 4
비교함수 구현 시 자주하는 실수
1. a가 b의 앞에 와야할 때만 true를 반환한다. a==b 라면 false를 반환한다.
예를 들어, 수열을 크기의 내림차순으로 정렬하고 싶을 때, a와 b가 같은 경우 false를 반환하니까 런타임 에러가 발생할 수 있다.
bool cmp(int a, int b){ // [런타임 에러 발생]
if(a >= b) return true;
return false;
}
a > b 일 때 true를 반환하도록 수정하자.
bool cmp(int a, int b){ // 올바른 형태
return a > b;
}
2. 비교 함수의 인자로 STL 혹은 클래스 객체를 전달시 reference를 사용하자.
문자열을 받아서 끝자리의 알파벳 순으로 정렬하고 싶어서 비교함수를 작성했다.
아래의 비교함수는 어떻게 개선하면 좋을까?
bool cmp(string a, string b){
return a.back() < b.back();
}
// 함수의 인자로 STL이나 구조체 객체를 보내면 값의 '복사'가 일어난다!
함수의 인자로 STL이나 구조체 객체를 보내면 값의 '복사'가 일어남을 기억하자!
이 경우, 굳이 복사라는 불필요한 연산이 없어도 비교가 가능하다.
따라서 복사를 하는 대신 아래처럼 reference를 보내는 것이 더 바람직하다.
bool cmp(const string& a, const string& b){ // 레퍼런스를 보내서 비교하는 것이 효율적.
return a.back() < b.back();
}
const string& a, const string& b라고 쓰면 a와 b는 변하지 않음을 명시적으로 나타내기 때문에 가독성이 도움이 된다.
// http://boj.kr/f3feaf22016f4c9687b84ab6be2f4389
#include <bits/stdc++.h>
using namespace std;
int n;
long long a[100005];
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for(int i = 0; i < n; i++)
cin >> a[i];
sort(a, a+n);
int cnt = 0;
long long mxval = -(1ll << 62) - 1; // 1을 long long으로 형변환하지 않고 1 << 62로 작성시 int overflow 발생
int mxcnt = 0;
for(int i = 0; i < n; i++){
if(i == 0 || a[i-1] == a[i]) cnt++; // i가 0인 경우 앞쪽 식이 true이기 때문에 a[i-1]을 참조하지 않음
else{
if(cnt > mxcnt){
mxcnt = cnt;
mxval = a[i-1];
}
cnt = 1;
}
}
if(cnt > mxcnt) mxval = a[n-1]; // 제일 마지막 수가 몇 번 등장했는지를 별도로 확인
cout << mxval;
}
코드 마지막 줄에 '제일 마지막 수가 몇 번 등장했는지 별도로 확인'해야 한다.
이 처리가 빠지면 제일 마지막 수의 등장 횟수를 빠뜨리게 된다.
이 문제에서 사용한 정렬을 하면, 같은 수는 인접하게 된다는 성질을 이용해 수열에서 중복된 원소를 제거할 수 도 있다.
#include <bits/stdc++.h>
using namespace std;
int n, num;
int arr[10001];
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for(int i = 0; i < n; i++){
cin >> num; arr[num]++;
}
for(int i = 1; i <= 10000; i++){
while(arr[i]){
cout << i << '\n'; arr[i]--;
}
}
return 0;
}
리뷰
이 문제는 sort() 함수를 쓰면 틀린다. 왜냐하면 메모리 제한이 8MB 이다. int 는 4bytes니까. 문제 조건인 천 만개 배열을 선언하면 40MB 를 차지한다. 10000 보다 작은 자연수만 입력으로 들어오니까, 10000을 배열의 인덱스로 두고. 인덱스에 해당하는 숫자의 '개수'를 배열의 값으로 저장해서 해결하면 된다.
[유의점] 쿠키 정보는 항상 서버에 전송된다! 네트워크 트래픽 추가 유발한다. 그래서 최소한의 정보만 사용해야 한다. (세션 id, 인증 토큰) 서버에 전송하지 않고, 웹 브라우저 내부에 데이터를 저장하고 싶으면 웹스토리지 참고. (localStorage, sessionStorage)
[주의점] 보안에 민감한 데이터는 저장하면 안됨(주민번호, 신용카드 번호 등)
쿠키의 생명주기
expries : 만료일이 되면 쿠기가 자동으로 삭제되게 지정 max-age : 0이나 음수를 지정하면 쿠키 삭제
쿠키의 종류 2가지
세션 쿠키 : 만료 날짜를 생략하면 브라우저 종료시까지 유지 영속 쿠키 : 만료 날짜를 입력하면 해당 날짜까지 유지
쿠키 도메인
특정 도메인을 명시하면 : 특정 도메인과 서브 도메인을 포함한 요청 시 해당 쿠키를 보낸다. 만약 도메인을 생략하면 : 현재 문서 기준 도메인만 적용한다.
쿠키 경로
이 경로를 포함한 하위 경로 페이지만 쿠키를 접근한다. 일반적으로 path=/ 루트로 지정한다. 하나의 도메인 내부의 ‘모든 경로’에서 쿠키를 쓰는 것이 일반적이기 때문이다.
쿠키와 관련된 보안3가지
1) Secure
Secure를 넣으면 https인 경우에만 쿠키를 서버로 전송한다.
2) HttpOnly
XSS 공격 방지
자바스크립트에서 쿠키에 접근 불가하도록 제한한다. HTTP 전송에만 사용한다.
3) SameSite
XSRF 공격 방지 요청 도메인과 쿠키에 설정된 도메인이 같은 경우에만 쿠키를 서버로 전송한다. SameSite는 가장 최근에 나온 것이라서 브라우저의 지원 여부를 확인하고 사용하자.