Pagini recente » Cod sursa (job #719913) | Cod sursa (job #3325565) | Cod sursa (job #312957) | Cod sursa (job #1298596) | Cod sursa (job #3349348)
/*
Solutie folosind KMP.
Odata cu introducerea la faculta in automate finite (doar introducere),
am citit despre automate nedeterministe, iar KMP-ul a devenit doar o
aplicatie directa a acestora
Recomand https://codeforces.com/blog/entry/146191 pentru intelegere si
https://leoriether.github.io/kmp-simulator/ pentru simulare si debug
Ideea de baza se rezuma la automatul finit nedeterminist care rezolva
problema de fata, pe care trebuie sa-l implementezi intr-un automat
determinist. Cunoscuta functie de prefix/pi este doar pozitia in care
te afli in NFA la aplicarea automatului asupra sirului B.
Daca vezi brut pentru fiecare cale(substring, de fapt) ce se intampla,
atunci ai o rezolvare in O(|A| * |B|).
Ideea principala este ca cea mai din dreapta pozitie in automat determina
unic restul pozitiilor din stanga ta, deoarece asta doar inseamna ca, tu,
concret, ai avansat doar n pasi, unde n reprezinta pozitia cea mai
din dreapta din automat in care s-ar putea afla un substring. E ca si cum
ai lua doar ultimul sufix de n caractere din sirul B
si-l aplici p-ala si te afli aici, restul caracterelor fiind de fapt degeaba.
Asa ca, tu poti doar sa te uiti la vecinii pe care ii ai de la pasul anterior
si sa vezi daca acestia avanseaza in automat sau daca mor. Aia care mor
nu pot sa influenteze vreodata altceva (erau de fapt substring-uri care
au intalnit un caracter care nu e in A la acea pozitie, deci nu mai conteaza),
iar noul vecin pentru pozitia curenta determina si el, la randul sau, unic
pe cei din stanga sa. Argumentul e fix la fel, intrucat, daca excluzi
pozitiile active din dreapta sa (adica un substring inceput candva mai
devreme), atunci ai doar un substring pe care ai aplicat automatul si
doar pasii astia sunt utili, deci ce e in stanga sa e unic determinat.
Deci poti construi brut "automatul" in felul asta. Analiza complexitatii
este iarasi simpla, deoarece fiecare activare a unui nod reprezinta
inceputul unui substring la fiecare pozitie 1, 2, ..., |A|, deci pot sa
moara doar |A| fire in automat. Cum firele pe care le vizitezi fie
mor acum, fie determina vecinul pozitiei curente, complexitatea devine
O(|A|). Alea care mor sunt cel mult |A|, iar determinarea vecinului se
face in O(1) pentru fiecare din cele |A| pozitii, deci, per total,
O(2 * |A|) = O(|A|).
Pentru a rezolva sirul B, doar simulam "automatul" pe care tocmai l-am
construit. Se intampla aceeasi poveste cu determinarea unica a celor
din stanga, insa trebuie sa fim atenti atunci cand avem
o aparitie a lui A in B si sa fim atenti la faptul ca a[i] nu e mereu b[i],
asa cum era la sirul A, unde stiam mereu ca cea mai din dreapta pozitie
avanseaza cu 1 la fiecare pas.
Pentru a construi automatul propriu-zis, ar trebui sa trasezi
sigma muchii pentru fiecare litera, care este pozitia la care ai ajunge
in automat, in cazul in care nu ai inainta. Pare ca, la fiecare pas,
variezi litera posibila, in loc de b[i] si faci aceeasi chestie, insa
e O(|A| * sigma).
*/
#include <fstream>
#include <cassert>
#include <vector>
#define ll long long
using namespace std;
const int NMAX = 2e6;
int pozitie[NMAX + 1];
int vecin[NMAX + 1];
vector <int> sol;
int main()
{
ifstream cin("strmatch.in");
ofstream cout("strmatch.out");
int i;
string a, b;
cin >> a >> b;
a = "!" + a;
b = "!" + b;
vecin[1] = 0;
vecin[0] = -1;
for (i = 2; i < a.size(); i++)
{
int ax = vecin[i - 1];
while (ax > -1 and a[ax + 1] != a[i])
ax = vecin[ax];
vecin[i] = ax + 1;
}
pozitie[0] = 0;
for (i = 1; i < b.size(); i++)
{
int ax = pozitie[i - 1];
if (ax == a.size())
ax = vecin[ax];
while (ax > -1 and a[ax + 1] != b[i])
ax = vecin[ax];
pozitie[i] = ax + 1;
}
for (i = 1; i < b.size(); i++)
if (pozitie[i] == a.size() - 1)
sol.push_back(i - 1 - pozitie[i] + 1);
cout << sol.size() << "\n";
sol.resize(min((int)sol.size(), 1000));
for (int x : sol)
cout << x << " ";
}