https://www.acmicpc.net/problem/3078
3078번: 좋은 친구
첫째 줄에 N과 K가 주어진다. (3 ≤ N ≤ 300,000, 1 ≤ K ≤ N) 다음 N개 줄에는 상근이네 반 학생의 이름이 성적순으로 주어진다. 이름은 알파벳 대문자로 이루어져 있고, 2글자 ~ 20글자이다.
www.acmicpc.net
등수의 차이가 $K$보다 작거나 같으면서 이름의 길이가 같은 친구들의 쌍 갯수를 세는 문제이다.
평범하게 생각하면 $O(N)$으로 훑어가면서 $O(2K)$에 등수의 차이가 작은 구간을 순회하게 되는데,
이 경우에는 $O(KN)$으로 TLE를 받아 제한시간 내에 문제를 해결할 수 없다.
투포인터를 활용한 스위핑으로 문제를 해결했다.
두개의 포인터 $l$ 과 $r$ 을 만드는데,
현재 위치가 $N$ 일때 $l$ 은 $N-K$ 이고,
$r$ 은 $N+K$ 으로 정해서 구간을 정해줄 수 있다.
stdu라는 배열을 하나 만들어서
stdu[k]를 구간 $(l,r]$ 내에 있는 이름의 길이가 $K$ 인 학생들의 수 로 정해준다.
$l$이 커질때 l 위치에 있는 학생의 이름 길이 $len(S_l)$ 에 대하여
stdu[$len(S_l)$] -= 1이 되고, 동일한 방법으로
stdu[$len(S_r)$] += 1이 된다,
그러면 stdu[k] 를 통해서 좋은 친구들의 수를 구할 수 있는데,
주의할 점 세가지로
(l,r] 으로 정했으니 폐구간 개구간 신경써서 스위핑해줘야 하고,
stdu[k] 안에 자기 자신도 포함되기 때문에 1씩 빼서 사람수를 세줘야 하고,
A학생과 B학생이 서로 친한 친구라면
A-B 로 한쌍, B-A로 한쌍이 중복으로 세지기 때문에
출력할때 반으로 나눠서 출력해주어야 한다.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int stdu[25] = {0};
string arr[345678];
int main(){
int N,K;
cin >> N >> K;
for(int i=1; i<=N; i++){
cin >> arr[i];
}
int l,r;
ll ans = 0;
for(int i=1; i<1+K; i++){
stdu[arr[i].length()]++;
}
for(int i=1; i<=N; i++){
l = max(0,i-K-1);
r = min(N+1,i+K);
stdu[arr[r].length()]++;
stdu[arr[l].length()]--;
//printf("[%s %d]", arr[i].c_str(), stdu[arr[i].length()]);
ans += stdu[arr[i].length()]-1;
}
cout << ans/2;
return 0;
}
개인적으로 딱 재밌는 난이도 정도의 문제라서
푸는데 매우 흡족했다.