Hangman 2
Pista 1
Notemos cómo el problema solo da una cota sobre . ¿Cuál es la cota sobre , respecto de ?
Pista 2
Partamos el problema en dos casos: y .
Explicación
Primero, notemos que porque . Esto será importante para analizar la complejidad temporal de nuestra solución. Ahora, consideremos los dos casos dados en la segunda pista.
Caso 1:
Como , podemos hacer fuerza bruta sobre cada par de strings y comprobar si difieren en no más de dos caracteres. Esto toma tiempo , que es equivalente a tiempo .
Caso 2:
Comparemos dos strings. Notemos que permitir que a lo sumo dos caracteres sean distintos es equivalente a ignorar dos caracteres en ambos strings y comprobar la igualdad. Así, podemos hacer fuerza bruta sobre cada par de caracteres que ignoramos, y luego comprobar la igualdad entre strings.
Sea el par de caracteres sobre el que hacemos fuerza bruta. Entonces, tenemos los siguientes intervalos que necesitan ser idénticos en ambos strings:
Ahora, si queremos comparar nuestros dos strings, solo necesitamos que las subcadenas sean iguales. Esto se puede comprobar usando hashing de strings.
Para cada string de nuestra lista, necesitamos hallar otro string que tenga los mismos hashes. Esto se hace mejor ordenando todas las tuplas de hashes, y luego comprobando ítems adyacentes y viendo si los hashes son iguales.
En total, esto toma tiempo . Con un poco más de análisis, se puede llegar a la complejidad temporal de .
Implementación
Complejidad temporal:
#include <bits/stdc++.h>
using namespace std;
// BeginCodeSnip{String Hashing Template (from the module)}
class HashedString {
private:
// change M and B if you want
static const long long M = 1e9 + 9;
static const long long B = 9973;
// pow[i] contains B^i % M
static vector<long long> pow;
// p_hash[i] is the hash of the first i characters of the given string
vector<long long> p_hash;
public:
HashedString(const string &s) : p_hash(s.size() + 1) {
while (pow.size() <= s.size()) { pow.push_back((pow.back() * B) % M); }
p_hash[0] = 0;
for (int i = 0; i < (int)s.size(); i++) {
p_hash[i + 1] = ((p_hash[i] * B) % M + s[i]) % M;
}
}
long long get_hash(int start, int end) {
long long raw_val = (p_hash[end + 1] - (p_hash[start] * pow[end - start + 1]));
return (raw_val % M + M) % M;
}
};
vector<long long> HashedString::pow = {1};
// EndCodeSnip
int main() {
int test_num;
cin >> test_num;
for (int t = 0; t < test_num; t++) {
int n, k;
cin >> n >> k;
vector<string> s(n);
for (string &i : s) { cin >> i; }
vector<bool> res(n);
if (n < k) {
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int diff_chars = 0;
for (int x = 0; x < k; x++) { diff_chars += (s[i][x] != s[j][x]); }
if (diff_chars <= 2) { res[i] = res[j] = true; }
}
}
} else {
vector<HashedString> hashed;
for (int i = 0; i < n; i++) { hashed.push_back(HashedString(s[i])); }
for (int i = 0; i < k; i++) {
for (int j = i + 1; j < k; j++) {
vector<array<long long, 4>> hashes(n);
for (int x = 0; x < n; x++) {
long long hash_1 = hashed[x].get_hash(0, i - 1);
long long hash_2 = hashed[x].get_hash(i + 1, j - 1);
long long hash_3 = hashed[x].get_hash(j + 1, k - 1);
hashes[x] = {hash_1, hash_2, hash_3, x};
}
sort(begin(hashes), end(hashes));
for (int x = 1; x < n; x++) {
bool works = true;
for (int y = 0; y < 3; y++) {
works &= hashes[x - 1][y] == hashes[x][y];
}
if (works) { res[hashes[x - 1][3]] = res[hashes[x][3]] = true; }
}
}
}
}
for (int i = 0; i < n; i++) { cout << res[i]; }
cout << "\n";
}
}