Bibliothèque de modèles pour algorithmes et structures de données

Structures de données

Théorie des graphes

Flot maximal

Dinic

Implémentation de l'algorithme de Dinic pour le flot maximal.

int compteurArête = 1, nbNœuds, nbArêtes, flotMax, courant[N], distance[N], source, puits, listeAdjacence[N];
std::queue<int> file;
struct Arête {int dest, capacité, suivante;} arêtes[M*2];

void ajouterArête(int u, int v, int cap) {
    compteurArête++;
    arêtes[compteurArête].dest = v;
    arêtes[compteurArête].capacité = cap;
    arêtes[compteurArête].suivante = listeAdjacence[u];
    listeAdjacence[u] = compteurArête;
}

bool bfs() {
    while (!file.empty()) file.pop();
    for (int i = 1; i <= nbNœuds; i++) {
        courant[i] = listeAdjacence[i];
        distance[i] = 0;
    }
    file.push(source);
    distance[source] = 1;
    while (!file.empty()) {
        int nœud = file.front();
        file.pop();
        for (int i = listeAdjacence[nœud]; i; i = arêtes[i].suivante) {
            int voisin = arêtes[i].dest;
            if (arêtes[i].capacité && !distance[voisin]) {
                distance[voisin] = distance[nœud] + 1;
                if (voisin == puits) return true;
                file.push(voisin);
            }
        }
    }
    return false;
}

int dfs(int nœud, int reste) {
    if (!reste || nœud == puits) return reste;
    int total = 0;
    for (int i = courant[nœud]; i; i = arêtes[i].suivante) {
        int voisin = arêtes[i].dest;
        courant[nœud] = i;
        int flux;
        if (distance[voisin] == distance[nœud] + 1 && (flux = dfs(voisin, std::min(reste, arêtes[i].capacité)))) {
            arêtes[i].capacité -= flux;
            arêtes[i^1].capacité += flux;
            total += flux;
            reste -= flux;
            if (reste == 0) break;
        }
    }
    if (total == 0) distance[nœud] = 0;
    return total;
}

void dinic() {while (bfs()) flotMax += dfs(source, INF);}</int>

Algorithmes pour chaînes de caractères

Représentation minimale

Algorithme pour trouver la rotation cyclique minimale d'une chaîne.

int tableau[2*N];
int longueur;

void initialiser() {
    scanf("%d", &longueur);
    for (int i = 1; i <= longueur; i++) {
        long long valeur;
        scanf("%lld", &valeur);
        tableau[i] = tableau[i + longueur] = valeur;
    }
}

void résoudre() {
    int i = 1, j = 2, k = 0;
    while (i <= longueur && j <= longueur) {
        k = 0;
        while (tableau[i+k] == tableau[j+k] && k < longueur) k++;
        if (tableau[i+k] > tableau[j+k]) i = i + k + 1;
        else j = j + k + 1;
        if (k == longueur) break;
        if (i == j) j++;
    }
    int index = std::min(i, j);
    for (int p = 1; p <= longueur; p++) {
        printf("%d ", tableau[index + p - 1]);
    }
}

Hachage de chaînes

Utilisation de hachage pour comparer des chaînes efficacement.

const unsigned long long base = 998244353;

std::string chaîne;
unsigned long long hashActuel, stockage[100005];
int compteur, résultat, nbChaînes;

void calculerHash() {
    hashActuel = 0;
    unsigned long long puissance = 1;
    for (size_t i = 0; i < chaîne.size(); i++) {
        hashActuel += chaîne[i] * puissance;
        puissance *= base;
    }
}

void comparer() {
    bool trouvé = false;
    for (int i = 1; i <= compteur; i++) {
        if (stockage[i] == hashActuel) {
            trouvé = true;
            break;
        }
    }
    if (!trouvé) {
        résultat++;
        stockage[++compteur] = hashActuel;
    }
}

void traiter() {
    for (int i = 1; i <= nbChaînes; i++) {
        std::cin >> chaîne;
        calculerHash();
        comparer();
    }
    printf("%d", résultat);
}

KMP (Knuth-Morris-Pratt)

Recherche de motif dans une chaîne avec l'algorithme KMP.

char motif[1000005], texte[1000005];
int longueurMotif, longueurTexte;
int bord[1000005];

void lire() {
    std::cin >> (texte+1) >> (motif+1);
    longueurMotif = strlen(motif+1);
    longueurTexte = strlen(texte+1);
}

void prétraitement() {
    bord[1] = 0;
    for (int i = 2, j = 0; i <= longueurMotif; i++) {
        while (j && motif[j+1] != motif[i]) j = bord[j];
        if (motif[j+1] == motif[i]) j++;
        bord[i] = j;
    }
}

void recherche() {
    for (int i = 1, j = 0; i <= longueurTexte; i++) {
        while (j && motif[j+1] != texte[i]) j = bord[j];
        if (motif[j+1] == texte[i]) j++;
        if (j == longueurMotif) printf("%d\n", i - j + 1);
    }
}

void afficherBords() {
    for (int i = 1; i <= longueurMotif; i++) {
        printf("%d ", bord[i]);
    }
}

Extension KMP (Fonction Z)

Algorithme pour calculer les correspondances préfixes étendues.

char source[N], motif[N];
int zTable[N], pTable[N];
int longueurSource, longueurMotif;

void initialiser() {
    std::cin >> (source+1) >> (motif+1);
    longueurSource = strlen(source+1);
    longueurMotif = strlen(motif+1);
}

void calculerZ() {
    zTable[1] = longueurMotif;
    for (int i = 2, gauche = 0, droite = 0; i <= longueurMotif; i++) {
        if (i <= droite) zTable[i] = std::min(zTable[i-gauche+1], droite-i+1);
        while (motif[1+zTable[i]] == motif[i+zTable[i]]) zTable[i]++;
        if (i+zTable[i]-1 > droite) { gauche = i; droite = i+zTable[i]-1; }
    }
}

void calculerP() {
    for (int i = 1, gauche = 0, droite = 0; i <= longueurSource; i++) {
        if (i <= droite) pTable[i] = std::min(zTable[i-gauche+1], droite-i+1);
        while (i+pTable[i] <= longueurSource && 1+pTable[i] <= longueurMotif && source[i+pTable[i]] == motif[1+pTable[i]]) pTable[i]++;
        if (i+pTable[i]-1 > droite) { gauche = i; droite = i+pTable[i]-1; }
    }
}

Manacher

Algorithme pour trouver le plus long palindrome dans une chaîne.

int rayons[2*N];
char entrée[N];
char transformé[2*N];
int longueur;

void préparer() {
    std::cin >> (entrée+1);
    transformé[0] = '$';
    int k = 1;
    transformé[k] = '#';
    longueur = strlen(entrée+1);
    for (int i = 1; i <= longueur; i++) {
        transformé[++k] = entrée[i];
        transformé[++k] = '#';
    }
    longueur = k;
}

void manacher() {
    rayons[1] = 1;
    for (int i = 2, centre = 1, bord = 1; i <= longueur; i++) {
        if (i <= bord) rayons[i] = std::min(rayons[2*centre-i], bord-i+1);
        while (transformé[i+rayons[i]] == transformé[i-rayons[i]]) rayons[i]++;
        if (i+rayons[i]-1 > bord) { centre = i; bord = i+rayons[i]-1; }
    }
}

Arbre préfixe (Trie)

Structure pour stocker et rechercher des chaînes efficacement.

int nbÉléments, nbRequêtes, arbre[N][150], compteurNoeuds, occurrence[N];

inline int encoder(char c) { return c - 'A'; }

void insérer(const std::string& s) {
    int courant = 0;
    for (char ch : s) {
        int code = encoder(ch);
        if (!arbre[courant][code]) arbre[courant][code] = ++compteurNoeuds;
        courant = arbre[courant][code];
    }
    occurrence[courant]++;
}

int rechercher(const std::string& s) {
    int courant = 0;
    for (char ch : s) {
        int code = encoder(ch);
        if (!arbre[courant][code]) return 0;
        courant = arbre[courant][code];
    }
    return occurrence[courant];
}

Arbre XOR 01

Variante de l'arbre préfixe pour opérations sur bits.

int nbÉléments, nbRequêtes, arbre[N][2], compteurNoeuds, compteur[N];

void insérer(int valeur) {
    int courant = 0;
    for (int i = 30; i >= 0; i--) {
        int bit = (valeur >> i) & 1;
        if (!arbre[courant][bit]) arbre[courant][bit] = ++compteurNoeuds;
        courant = arbre[courant][bit];
    }
    compteur[courant]++;
}

int rechercher(int valeur) {
    int courant = 0, résultat = 0;
    for (int i = 30; i >= 0; i--) {
        int bit = (valeur >> i) & 1;
        if (arbre[courant][bit^1]) { courant = arbre[courant][bit^1]; résultat |= (1 << i); }
        else courant = arbre[courant][bit];
    }
    return résultat;
}

Automate Aho-Corasick

Recherche de multiple motifs dans un texte avec l'algorithme Aho-Corasick.

int trie[(int)1e6+6][30], lien[(int)1e6+5], compteur, nbMotifs;
std::string texte, motifs[(int)1e6+6];

void lire();
void ajouterMotif(const std::string& s);
void construireTrie();
std::queue<int> file;
void construireLiens();
int rechercher(const std::string& texte);

int main() {
    lire();
    construireTrie();
    construireLiens();
    std::cout << rechercher(texte);
    return 0;
}

int rechercher(const std::string& s) {
    int total = 0;
    int courant = 0;
    for (char c : s) {
        int idx = c - 'a';
        courant = trie[courant][idx];
        for (int j = courant; compteur[j] != -1 && j; j = lien[j]) {
            total += compteur[j];
            compteur[j] = -1;
        }
    }
    return total;
}

void ajouterMotif(const std::string& s) {
    int courant = 0;
    for (char c : s) {
        int idx = c - 'a';
        if (!trie[courant][idx]) trie[courant][idx] = ++compteur;
        courant = trie[courant][idx];
    }
    compteur[courant]++;
}

void construireTrie() {
    for (int i = 1; i <= nbMotifs; i++) ajouterMotif(motifs[i]);
}

void construireLiens() {
    for (int i = 0; i < 26; i++) {
        if (trie[0][i]) file.push(trie[0][i]);
    }
    while (!file.empty()) {
        int u = file.front();
        file.pop();
        for (int i = 0; i < 26; i++) {
            int v = trie[u][i];
            if (v) {
                lien[v] = trie[lien[u]][i];
                file.push(v);
            } else trie[u][i] = trie[lien[u]][i];
        }
    }
}

void lire() {
    std::cin >> nbMotifs;
    for (int i = 1; i <= nbMotifs; i++) std::cin >> motifs[i];
    std::cin >> texte;
}</int>

Tableau des suffixes (SA)

Construction du tableau des suffixes avec l'algorithme de tri par comptage.

#include <bits>
using namespace std;

const int MAX = 3e6;
int longueur, tailleSeau = 256;
int seau[MAX], temp[MAX], comptage[MAX], sa[MAX], rang[MAX], lcp[MAX];
char chaîne[MAX];

void lire();
void construireSA();
void construireLCP();

int main() {
    lire();
    construireSA();
    for (int i = 1; i <= longueur; i++) printf("%d ", sa[i]);
    printf("\n");
    construireLCP();
    for (int i = 1; i <= longueur; i++) printf("%d ", lcp[i]);
    return 0;
}

void lire() {
    std::cin >> (chaîne+1);
    longueur = strlen(chaîne+1);
}

void construireSA() {
    for (int i = 1; i <= longueur; i++) comptage[seau[i] = chaîne[i]]++;
    for (int i = 1; i <= tailleSeau; i++) comptage[i] += comptage[i-1];
    for (int i = longueur; i >= 1; i--) sa[comptage[seau[i]]--] = i;
    for (int k = 1; k <= longueur; k <<= 1) {
        memset(comptage, 0, sizeof(comptage));
        for (int i = 1; i <= longueur; i++) temp[i] = sa[i];
        for (int i = 1; i <= longueur; i++) comptage[seau[temp[i]+k]]++;
        for (int i = 1; i <= tailleSeau; i++) comptage[i] += comptage[i-1];
        for (int i = longueur; i >= 1; i--) sa[comptage[seau[temp[i]+k]]--] = temp[i];
        memset(comptage, 0, sizeof(comptage));
        for (int i = 1; i <= longueur; i++) temp[i] = sa[i];
        for (int i = 1; i <= longueur; i++) comptage[seau[temp[i]]]++;
        for (int i = 1; i <= tailleSeau; i++) comptage[i] += comptage[i-1];
        for (int i = longueur; i >= 1; i--) sa[comptage[seau[temp[i]]]--] = temp[i];
        tailleSeau = 0;
        for (int i = 1; i <= longueur; i++) temp[i] = seau[i];
        for (int i = 1; i <= longueur; i++) {
            if (temp[sa[i]] == temp[sa[i-1]] && temp[sa[i]+k] == temp[sa[i-1]+k]) seau[sa[i]] = tailleSeau;
            else seau[sa[i]] = ++tailleSeau;
        }
        if (tailleSeau == longueur) break;
    }
}

void construireLCP() {
    for (int i = 1; i <= longueur; i++) rang[sa[i]] = i;
    for (int i = 1, k = 0; i <= longueur; i++) {
        if (rang[i] == 1) continue;
        if (k) k--;
        int j = sa[rang[i]-1];
        while (i+k <= longueur && j+k <= longueur && chaîne[i+k] == chaîne[j+k]) k++;
        lcp[rang[i]] = k;
    }
}</bits>

Automate des suffixes (SAM)

Construction de l'automate des suffixes pour des applications avancées.

#define ll long long
#define MAX_N (int(2e6+6))

int lien[MAX_N], transitions[MAX_N][28], longueur[MAX_N], compteur[MAX_N], idx = 1, courant = 1;
ll résultat;
std::string chaîne;
std::vector<int> arbre[MAX_N];

void étendre(int c);
void parcourir(int u);

int main() {
    std::cin >> chaîne;
    for (char c : chaîne) étendre(c - 'a');
    for (int i = 2; i <= idx; i++) arbre[lien[i]].push_back(i);
    parcourir(1);
    std::cout << résultat;
    return 0;
}

void parcourir(int u) {
    for (int v : arbre[u]) {
        parcourir(v);
        compteur[u] += compteur[v];
    }
    if (compteur[u] > 1) résultat = std::max(résultat, (ll)compteur[u] * longueur[u]);
}

void étendre(int c) {
    int p = courant;
    courant = ++idx;
    longueur[courant] = longueur[p] + 1;
    compteur[courant] = 1;
    for (; p && !transitions[p][c]; p = lien[p]) transitions[p][c] = courant;
    if (!p) lien[courant] = 1;
    else {
        int q = transitions[p][c];
        if (longueur[q] == longueur[p] + 1) lien[courant] = q;
        else {
            int copie = ++idx;
            longueur[copie] = longueur[p] + 1;
            lien[copie] = lien[q];
            lien[q] = lien[courant] = copie;
            for (; p && transitions[p][c] == q; p = lien[p]) transitions[p][c] = copie;
            memcpy(transitions[copie], transitions[q], sizeof(transitions[q]));
        }
    }
}</int>

Théorie des nombres et mathématiques

PGCD optimal

Algorithme rapide pour calculer le plus grand commun diviseur.

ll pgcd(ll a, ll b) {
    while (b) { a %= b; std::swap(a, b); }
    return a;
}

Algorithmes de tri

Tri rapide

Implémentation du tri rapide en place.

int tableau[N];

void triRapide(int gauche, int droite) {
    if (gauche >= droite) return;
    int i = gauche, j = droite;
    int pivot = tableau[gauche];
    while (i < j) {
        while (i < j && tableau[j] >= pivot) j--;
        tableau[i] = tableau[j];
        while (i < j && tableau[i] <= pivot) i++;
        tableau[j] = tableau[i];
    }
    tableau[i] = pivot;
    triRapide(gauche, i-1);
    triRapide(i+1, droite);
}

Tri fusion

Implémantation du tri fusion avec comptage des inversions.

int source[N], buffer[N], inversions;

void triFusion(int gauche, int droite) {
    if (gauche >= droite) return;
    int milieu = (gauche + droite) >> 1;
    triFusion(gauche, milieu);
    triFusion(milieu+1, droite);
    int i = gauche, j = milieu+1, k = gauche;
    while (i <= milieu && j <= droite) {
        if (source[i] <= source[j]) buffer[k++] = source[i++];
        else { buffer[k++] = source[j++]; inversions += milieu - i + 1; }
    }
    while (i <= milieu) buffer[k++] = source[i++];
    while (j <= droite) buffer[k++] = source[j++];
    for (int i = gauche; i <= droite; i++) source[i] = buffer[i];
}

Entrées/Sorties rapides

Lecture et écriture accélérées

Fonctions pour une E/S plus rapide en compétition.

typedef long long lll;

lll lire() {
    bool négatif = false;
    char c = getchar();
    lll résultat = 0;
    while (!isdigit(c) && c != EOF) {
        négatif = (c == '-');
        c = getchar();
    }
    while (isdigit(c) && c != EOF) {
        résultat = (résultat << 3) + (résultat << 1) + (c ^ '0');
        c = getchar();
    }
    return négatif ? -résultat : résultat;
}

int tampon[100];
void écrire(lll x) {
    int i = 0;
    if (x < 0) { x = -x; putchar('-'); }
    if (x == 0) putchar('0');
    while (x) { tampon[++i] = x % 10; x /= 10; }
    while (i) putchar(tampon[i--] + '0');
}

Étiquettes: Dinic KMP Suffix Array Automate des suffixes Aho-Corasick

Publié le 30 mai à 10h36