/*
String match - Implementare algoritm
------------------------------------
## Problema de baza
https://www.infoarena.ro/problema/strmatch
## Idee
### Naiv -> O(n*k)
mic = "ABA" = lungimea k
mare = "CABBCABABAB" = lungimea n
Iteram prin mare, luand fiecare pozitie in parte.
La o pozitie i, verificam daca mic apare in mare incepand pe pozitia i.
### Eficient
Exista si alte idei, KMP si Z-Algorithm.
Acum folosim Hashuri, folosind invers/aritmetica modulara.
Hash = o functie care converteste o informatie intr-o alta informatie, de regula mai mica, aproape unica si foarte greu de reverseuit.
Vrem sa facem o functie hash care converteste un sir de lungime k intr-un numar int.
Exemplu (ipotetic)
"ABA" -> 32722398
"CAB" -> 23892192
"AAA" -> 94384387
Daca reusim sa aflam hashul unui sir mai eficient, am putea sa comparam doua siruri de caractere de lungime k instant, comparandu-le hashurile.
### Hashing function
Urmatoarea functie de hash, nu foarte complicata, dar faina si utila, cu acuratete mare:
MOD =ex= 1e9 + 7, 1e9 + 9
MULT =ex= 811, 911, 997, etc.
functia noastra de hash pe un sir s de lungime k va returna urmatoarea formula:
(s[0] * MULT^{k - 1} + s[1] * MULT^{k - 2} + s[2] * MULT^{k - 3} + ... + s[k - 2] * MULT + s[k - 1] * MULT^0) modulo MOD
Exemplu: mare = "ABAE"
MULT = 97, s = "ABA" => k = 3, MOD = 666013
hash("ABA") = (s[0] * MULT^2 + s[1] * MULT^1 + s[2] * MULT^0) modulo MOD
hash("ABA") = (65 * 97^2 + 66 * 97 + 65) modulo 666013 // = 618052
Si daca am vrea sa convertim de la "ABA" la "BAE" in O(1)
s = "BAE"
hash("BAE") = (66 * 97^2 + 65 * 97 + 69) modulo 666013 // = 627368
hash("BAE") = ((hash("ABA") - 66 * 97^2) * 97 + 69) modulo 666013
Complexitate hash: O(k)
La inceput:
ans = 0
La fiecare pas:
ans = (ans * MULT + s[i]) modulo MOD;
ans = (0 * MULT + s[0]) modulo MOD = s[0]
ans = s[0] * MULT + s[1]
ans = s[0] * MULT^2 + s[1] * MULT + s[2]
ans = s[0] * MULT^3 + s[1] * MULT^2 + s[2] * MULT + s[3]
...
ans = s[0] * MULT^{k - 1} + s[1] * MULT^{k - 2} + s[2] * MULT^{k - 3} + ... + s[k - 2] * MULT + s[k - 1]
Cand trecem de la CAB la ABB:
ans = ((ans - s[0] * MULT^{k - 1}) * MULT + s[k]) modulo MOD
*/
#include <bits/stdc++.h>
#define NMAX 2000000
#define MULT1 811
#define MULT2 997
#define MOD1 1000000007
#define MOD2 1000000009
using namespace std;
ifstream fin("strmatch.in");
ofstream fout("strmatch.out");
char mic[NMAX + 1], mare[NMAX + 1];
int k, n; // |mic| = k, |mare| = n (lungimea lui mic = k, lungimea lui mare = n)
//Practic pentru fiecare numar folosim 2 hashuri, unul cu MULT1 / MOD1 si unul cu MULT2 / MOD2.
struct hashuire {
int val1, val2;
} hashMic, hashSecvMare;
int powlog(int baza, int expo, int MOD) {
int ans = 1;
for(int i = 1; i <= expo; i <<= 1) {
if(i & expo) ans = 1ll * baza * ans % MOD;
baza = 1ll * baza * baza % MOD;
}
return ans;
}
int constructHash(int MULT, int MOD, char s[]) {
// Dandu-se un sir s, sa se construiasca hashul primelor k caractere (de pe pozitiile 0 .. k - 1), folosind MULT si MOD
// hashMic.val1 / val2 = constructHash(MULT, MOD, mic)
// hashSecvMare.val1 / val2 = constructHash(MULT, MOD, mare)
int ans = 0;
for(int i = 0; i < k; i++)
ans = (1ll * ans * MULT + s[i]) % MOD;
return ans;
}
int updateHash(int MULT, int MOD, int oldHash, char oldChar, char newChar) {
// Dandu-se un hash oldHash, sa se actualizeze in asa fel incat sa se scoata primul caracter (oldChar) si sa se bage un nou ultim caracter (newChar)
// hash("BAE") = updateHash(MULT, MOD, hash("ABA"), 'A', 'E')
// 627368 = updateHash(MULT, MOD, 618052, 'A', 'E')
//Formula (de mai sus): ans = ((ans - s[0] * MULT^{k - 1}) * MULT + s[k]) modulo MOD
return (1ll * (oldHash - 1ll * oldChar * powlog(MULT, k - 1, MOD)) % MOD * MULT % MOD + newChar) % MOD;
}
bool areHashesEqual(hashuire a, hashuire b) {
return a.val1 == b.val1 && a.val2 == b.val2;
}
void citire() {
fin >> mic >> mare;
}
void procesare() {
k = strlen(mic);
n = strlen(mare);
hashMic.val1 = constructHash(MULT1, MOD1, mic);
hashMic.val2 = constructHash(MULT2, MOD2, mic);
hashSecvMare.val1 = constructHash(MULT1, MOD1, mare);
hashSecvMare.val2 = constructHash(MULT2, MOD2, mare);
vector<int> poz; // Pozitiile din mare unde apare mic
if(areHashesEqual(hashMic, hashSecvMare)) {
poz.push_back(0);
}
for(int i = 1; i <= n - k; i++) {
hashSecvMare.val1 = updateHash(MULT1, MOD1, hashSecvMare.val1, mare[i - 1], mare[i - 1 + k]);
hashSecvMare.val2 = updateHash(MULT2, MOD2, hashSecvMare.val2, mare[i - 1], mare[i - 1 + k]);
if(areHashesEqual(hashMic, hashSecvMare)) {
poz.push_back(i);
}
}
fout << poz.size() << "\n";
if(poz.size() > 1000)
for(int i = 0; i < 1000; i++)
fout << poz[i] << " ";
else
for(int i = 0; i < poz.size(); i++)
fout << poz[i] << " ";
}
int main()
{
citire();
procesare();
return 0;
}