비트마스킹, 백트래킹
먼저 예외부터 정리하자. 이하인 경우는 단어를 배울 수 없다. 모든 남극의 단어는 anta~tica
의 형태이며, a
, n
,t
,i
,c
의 단어는 무조건 알아야 단어를 배우는 것이 가능하다. 반대로 표현하자면 보다 작은 경우는 탐색 할 필요없이 을 출력하면 된다.
어떤 단어든 a
, n
, t
, i
, c
를 배운다. 즉 5개는 먼저 배운상태로 진행해도 탐색에 지장을 주지 않는다. 위의 5문자를 알파벳 문자열 순서로 이진화하면, 이 나온다.
부분집합으로도 정답은 나오겠지만 26 개 정도의 부분집합은 분명 시간초과가 나올 수 밖에 없다. 정답이 나오는 요소는 만큼 문자를 아는 것이 가장 단어를 많이 알 수 있는 것에 가깝기 때문에 여기서는 조합을 사용하는 것이 좋다.
현재 알파벳이 포함되어 있는 경우를 제외하고, 포함하지 않는 경우의 문자만 하나씩 추가하여 조합을 진행한다. 단, 보다 빠른 계산을 위해 초기 시작값을 a
, n
, t
, i
, c
를 이미 가지고 시작하는 것으로 했다.
임의의 개 만큼 알파벳을 알고 있을 때, 현재 문자를 아는지 모르는지 판별하는 방법은 두개의 정수를 이진화하여 비교했을 때, 하나의 정수와 값이 완전 일치할 때 이다. (curr & a[i]) == a[i]
, (curr | a[i]) == curr
#include <iostream>
#include <vector>
using namespace std;
int N, K, a[51],ans;
//int t
void input() {
string s;
for (int i = 0; i < N; i++) {
cin >> s;
for (int j = 0; j < s.size(); j++) {
int num = (1 << (int) (s[j] - 'a'));
a[i] |= a[i] & num ? 0 : num;
// t |= t & num ? 0 : num;
}
}
}
void solve(int d, int idx, int curr) {
if (d >= K) {
int cnt = 0;
for (int i = 0; i < N; i++) if ((curr & a[i]) == a[i]) cnt++;
ans = max(ans, cnt);
return;
}
for (int i = idx; i < 26; i++) {
int num = 1 << i;
if (curr & num) continue;
//if (!(t & num)) continue;
solve(d + 1, i + 1, curr | num);
}
}
void output() {
cout << ans;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> N >> K;
if (K < 5) {
cout << 0;
return 0;
}
input();
solve(5, 0, 532741);
output();
}