Check Transcription
Explicación
Para resolver este problema, podemos usar fuerza bruta y comprobar cada longitud posible de . Sean y el número de ceros y unos en , respectivamente. La longitud de se puede determinar por . Si esta longitud no es un entero, entonces este es inválido y no hace falta comprobarlo más.
Para cada longitud posible de , queremos comprobar si el patrón dado en coincide con la señal recibida . Sea el hash de igual a . Precalculando las sumas de prefijos de los hashes de , podemos recorrer y comprobar para cada carácter en si el hash de su parte correspondiente en es igual a . Esta parte correspondiente en se puede localizar si usamos un puntero y lo incrementamos en cada vez que comprobamos un carácter de . Al final, si todos los hashes coinciden, entonces encontramos un posible. En caso contrario, si hay un desajuste en algún punto, podemos terminar e ir a la siguiente longitud posible de .
Complejidad temporal
Primero notamos que comprobar un posible se puede hacer en porque para cada carácter en , podemos obtener el hash de su subcadena correspondiente en en , dado un preprocesamiento para calcular las sumas de prefijos de los hashes.
Luego, determinemos el número de longitudes posibles de . Sin pérdida de generalidad, supongamos . Como y son estrictamente positivos, debe ser menor o igual que .
Por lo tanto, la complejidad temporal total es .
Implementación
#include <bits/stdc++.h>
using namespace std;
using ull = unsigned long long int;
ull m = 1e9 + 9;
ull base = 9973;
// Hashes and powers start with 1
vector<ull> rolling_hash;
vector<ull> powers;
/** Calculate the prefix sums of the hashes for the given string t. */
void generate_hash(string &t) {
rolling_hash.push_back(0);
powers.push_back(1);
for (int i = 0; i < t.size(); i++) {
ull current_hash = ((rolling_hash.back() * base) % m + (ull)t[i]) % m;
rolling_hash.push_back(current_hash);
powers.push_back(powers.back() * base % m);
}
}
/** Get the hash value of the substring (a,b] */
ull get_hash(int a, int b) {
ull previous = rolling_hash[a] * powers[b - a] % m;
// Add m in case rolling_hash[b] < previous
return ((m + rolling_hash[b]) - previous) % m;
}
int main() {
string s, t;
cin >> s >> t;
generate_hash(t);
int count0 = 0;
int count1 = 0;
for (char &c : s) {
if (c == '0') {
count0++;
} else if (c == '1') {
count1++;
}
}
int pairs_count = 0;
// Brute force through all possible lengths of r0
for (int len_r0 = 1; len_r0 < t.size() / count0; len_r0++) {
// This r0 is invalid if the length of r1 is no longer an integer
if ((t.size() - len_r0 * count0) % count1 != 0) { continue; }
int len_r1 = (t.size() - len_r0 * count0) / count1;
int len_ri[2] = {len_r0, len_r1};
ull hash_i[2] = {0, 0};
// pointer_t points to the current position in the received signal t.
int pointer_t = 0;
// i points to the current position in the pattern string s.
for (int i = 0; i < s.size(); i++) {
int s_i = s[i] - '0';
// If the hash for r_i is not calculated yet, we first calculate
// this.
if (hash_i[s_i] == 0) {
hash_i[s_i] = get_hash(pointer_t, pointer_t + len_ri[s_i]);
}
/*
* Compare the hash of r_i with the current substring in t. If they
* are not the same, or if both hashes (and therefore both r_0 and
* r_1) are the same, this r_0 is invalid and we can go to the next
* possibility.
*/
if (hash_i[s_i] != get_hash(pointer_t, pointer_t + len_ri[s_i]) ||
hash_i[0] == hash_i[1]) {
/*
* Since we add one to the counter anyway, we want to subtract
* one in case this r_0 does not count.
*/
pairs_count--;
break;
}
// Move the pointer to the start of next token.
pointer_t += len_ri[s_i];
}
pairs_count++;
}
cout << pairs_count << endl;
}