Programare Competitivă

Tehnica Two Pointers (doi indici)

Two Pointers este o tehnică în care folosim doi indici care se deplasează prin vector, de obicei în aceeași direcție sau din direcții opuse. În loc să verificăm toate perechile posibile (O(n^2)), găsim răspunsul în O(n).


Când folosim Two Pointers?

  • Când vectorul este sortat
  • Când căutăm perechi cu o proprietate (sumă, diferență)
  • Când vrem să eliminăm duplicate
  • Când procesăm două secvențe simultan

Varianta 1: doi indici din direcții opuse

Problema: pereche cu suma S

Avem un vector sortat și un număr S. Găsim două elemente a căror sumă este S.

Ideea

  • Punem un indice st la început și un indice dr la sfârșit.
  • Calculăm v[st] + v[dr].
  • Dacă suma este prea mică, mutăm st la dreapta (creștem suma).
  • Dacă suma este prea mare, mutăm dr la stânga (micșorăm suma).
  • Dacă suma este exact S, am găsit!
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");

int v[100001];
int n, s;

int main()
{
    fin >> n >> s;
    for (int i = 1; i <= n; i++) {
        fin >> v[i];
    }

    int st = 1, dr = n;
    int gasit = 0;

    while (st < dr) {
        int suma = v[st] + v[dr];

        if (suma == s) {
            fout << v[st] << " + " << v[dr] << " = " << s;
            gasit = 1;
            break;
        } else if (suma < s) {
            st++;
        } else {
            dr--;
        }
    }

    if (gasit == 0) {
        fout << "NU";
    }

    return 0;
}

date.in:

7 12
1 3 4 5 7 9 11

date.out:

3 + 9 = 12

Pas cu pas

st dr v[st] v[dr] Suma Acțiune
1 7 1 11 12 GĂSIT!

De fapt, în acest caz găsim imediat. Alt exemplu cu S = 10:

st dr v[st] v[dr] Suma Acțiune
1 7 1 11 12 12 > 10 -> dr–
1 6 1 9 10 GĂSIT!

De ce funcționează?

Vectorul este sortat. Dacă suma e prea mică, singurul mod de a o crește este să mărim elementul mic (st++). Dacă e prea mare, singurul mod de a o micșora este să micșorăm elementul mare (dr–).

Complexitate: O(n) - fiecare indice se mișcă cel mult n pași. Comparativ, verificarea tuturor perechilor ar fi O(n^2).


Varianta 2: doi indici în aceeași direcție

Problema: cel mai lung subșir fără duplicate

Avem un vector și vrem să găsim cea mai lungă secvență de elemente consecutive în care nu există duplicate.

Ideea

Folosim doi indici st și dr care definesc o fereastră. Extindem dr la dreapta cât timp nu avem duplicate. Când apare un duplicat, mutăm st la dreapta până îl eliminăm.

#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");

int v[100001];
int fr[100001]; // frecventa valorilor in fereastra curenta
int n;

int main()
{
    fin >> n;
    for (int i = 1; i <= n; i++) {
        fin >> v[i];
    }

    int st = 1;
    int maxLung = 0;

    for (int dr = 1; dr <= n; dr++) {
        fr[v[dr]]++;

        while (fr[v[dr]] > 1) {
            fr[v[st]]--;
            st++;
        }

        int lung = dr - st + 1;
        if (lung > maxLung) {
            maxLung = lung;
        }
    }

    fout << maxLung;

    return 0;
}

date.in:

9
1 3 2 3 4 5 2 6 7

date.out:

5

Secvența 3 4 5 2 6 (pozițiile 4-8) are lungimea 5, fără duplicate.

Pas cu pas (simplificat)

dr v[dr] Fereastra Duplicate? st lung maxLung
1 1 [1] NU 1 1 1
2 3 [1,3] NU 1 2 2
3 2 [1,3,2] NU 1 3 3
4 3 [1,3,2,3] DA (3) mut st
[3,2,3] DA mut st
[2,3] NU 3 2 3
5 4 [2,3,4] NU 3 3 3
6 5 [2,3,4,5] NU 3 4 4
7 2 [2,3,4,5,2] DA (2) mut st
[3,4,5,2] NU 4 4 4
8 6 [3,4,5,2,6] NU 4 5 5
9 7 [3,4,5,2,6,7] NU 4 6 6

Răspuns: 6 (secvența 3,4,5,2,6,7).


Problema: numărul de perechi cu diferența D

Vector sortat. Câte perechi (v[i], v[j]) cu i < j au v[j] - v[i] = D?

#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");

int v[100001];
int n, d;

int main()
{
    fin >> n >> d;
    for (int i = 1; i <= n; i++) {
        fin >> v[i];
    }

    int st = 1;
    int cnt = 0;

    for (int dr = 2; dr <= n; dr++) {
        while (v[dr] - v[st] > d && st < dr) {
            st++;
        }
        if (v[dr] - v[st] == d) {
            cnt++;
        }
    }

    fout << cnt;

    return 0;
}

date.in:

7 3
1 2 3 4 5 6 7

date.out:

4

Perechile: (1,4), (2,5), (3,6), (4,7).


Problema: reuniunea a două mulțimi sortate

Am văzut deja la interclasare - doi indici care avansează pe doi vectori. Aceasta este tot o variantă de Two Pointers.

int i = 1, j = 1;

while (i <= n && j <= m) {
    if (a[i] < b[j]) {
        fout << a[i] << " ";
        i++;
    } else if (a[i] > b[j]) {
        fout << b[j] << " ";
        j++;
    } else {
        fout << a[i] << " "; // egal - afișăm o dată
        i++;
        j++;
    }
}
Two Pointers și interclasarea

Interclasarea este de fapt un caz particular de Two Pointers: doi indici care avansează pe doi vectori sortați.

La fel și căutarea binară poate fi văzută ca o variantă: doi indici (st, dr) care se apropie unul de celălalt.

Multe tehnici se bazează pe aceeași idee fundamentală: deplasarea inteligentă a unor indici.


Schema generală Two Pointers

Direcții opuse (vector sortat):

st -->            <-- dr
[  ...  ...  ...  ...  ]

Condiția: while (st < dr)

Aceeași direcție (fereastră):

st -->   dr -->
[  ...  ...  ...  ...  ]

Condiția: for dr de la 1 la n, st avansează când e necesar


Greșeli frecvente

1. Vectorul nu este sortat (când trebuie)

Varianta cu direcții opuse funcționează doar pe vectori sortați. Fără sortare, logica “crește st / scade dr” nu are sens.


2. Condiția st < dr vs st <= dr

  • La perechi: st < dr (nu vrem aceeași poziție)
  • La căutare binară: st <= dr (verificăm și elementul unic)

3. Indici care nu avansează

Dacă nu mutăm niciodată st sau dr, bucla devine infinită. La fiecare pas, cel puțin un indice trebuie să avanseze.


Ce să reții

  • Two Pointers folosește doi indici care se deplasează inteligent prin vector.
  • Direcții opuse (pe vector sortat): perechi cu suma/diferența dată.
  • Aceeași direcție (fereastră): secvențe fără duplicate, secvențe cu proprietăți.
  • Complexitate: O(n) în loc de O(n^2).
  • Vectorul trebuie sortat pentru varianta cu direcții opuse.
  • Este una dintre cele mai frecvente tehnici la concursuri.

Probleme

pbinfoSecvkdistincte

pbinfoSumainsecv

pbinfoRgb

pbinfoPalindrom4