Skip to content

Trie

Implementation

#include <bits/stdc++.h>
using namespace std;

// Εκφώνηση:
// Θα σας δείνεται ένα λεξικό απο Ν λέξεις.
// Στην συνέχεια θα σας δωθούν Q άλλες λέξεις και πρέπει να βρείτε αν ανήκουν
// στο λεξικό ή όχι.

// παράδειγμα εισόδου
/*
4
abcd
abb
ac
aaaa
3
aa
ac
abc
*/

// παράδειγμα εξόδου
/*
NO
YES
NO
*/

struct node {
    // Πίνακας απο δείκτες σε άλλους (παιδία) κόμβους του συγκεκριμένου κόμβου
    // του Trie. Έχουμε 26 lower case λατινικούς χαρακτήρες άρα θα έχουμε το
    // πολύ 26 παιδία αφού κάθε ακμή αναπαριστά την πρόσθεση ενός χαρακτήρα στο
    // τέλος της συμβολοσειράς μας.
    struct node *p[26];
    // Μεταβλητή που θα είναι αληθής εάν και μόνο εαν ο κόμβος μας είναι τέλος
    // λέξης.
    bool leksi;
};

int main() {

    // Δημιουργούμε τον αρχικό κόμβο του trie (η κενή συμβολοσειρά)
    // o default constructor θα δώσει όλες τις θέσεις του πίνακα start.p να
    // είναι ίσες με NULL (δηλαδή δεν δείχνουν πουθενά - άρα δεν υπάρχει ακμή
    // για το συγκεκριμένο γράμμα) επίσης η μεταβλητή start.leksi είναι false
    // (by default)
    node start = node();

    // Τώρα θα διαβάσουμε τις λέξεις και θα δημιουργήσουμε το λεξικό-trie
    int n;
    cin >> n;

    for (int i = 0; i < n; i++) {

        // Διαβάζουμε την λέξη
        string x;
        cin >> x;

        // Ξεκινούμε και για κάθε γράμμα της λέξης (ένα προς ένα)
        // το βάζουμε στο Trie ως ακμή στον τωρινό κόμβο.
        // Ξεκινούμε πάντα απο start. Άρα έχουμε δείκτη που να δείχνει στο
        // start. Το &start θα μας επιστρέψει την διεύθηνση του αρχικού κόμβου
        // την οποία θα αποθηκέυσουμε στον δείκτη μας. Θυμηθείται ότι ο pointer
        // είναι για να "δείχνει" σε διευθήνσεις μνήμης.
        node *torinos = &start;

        for (size_t j = 0; j < x.size(); j++) {

            // Ελέγχουμε εάν υπάρχει ακμή απο το τωρινό κόμβο για το γράμμα που
            // προσπαθούμε να προσθέσουμε
            if (torinos->p[x[j] - 'a'] == NULL) {
                // Εαν δεν υπάρχει πρέπει να δημιουργήσουμε τον κόμβο στον οποίο
                // θα δείχνει η ακμή. Το keyword "new" χρησιμοποιείται για να
                // δεσμεύσουμε καινούργια διεύθηνηση μνήμης για το instance του
                // struct μας και επιστρέφει δείκτη σε αυτήν την διεύθηνση.
                // Χωρίς new o constructor ναι μεν θα επέστρεφε καινούργιο
                // instance του node αλλά η μεταβλητή μας θα βρισκόταν στην ίδια
                // διεύθηνση με πριν το οποίο δεν θέλουμε να γίνει προφανώς.
                node *neos = new node();
                // Τώρα μπορούμε να βάλουμε την ακμή για το γράμμα μας να
                // δείχνει στον καινούργιο κόμβο
                torinos->p[x[j] - 'a'] = neos;
                // και ο καινούργιος κόμοβος γίνεται τώρα ο τωρινός στον οποίο
                // θα δείχνουμε
                torinos = neos;
            } else {
                // αφού η ακμή και ο κόμβος στον οποίο δέιχνει υπάρχει
                // πολύ απλά βάζουμε τον δείχτη μας να δείχνει σε αυτό τον κόμβο
                torinos = torinos->p[x[j] - 'a'];
            }
        }

        // Τώρα αφού έχουμε φτάσει να βάλουμε ακμή και για το τελευταίο γράμμα
        // της συμβολοσειράς x άρα σημαίνει δείχνουμε στον κόμβο που αναπαρτηστά
        // το x στο trie μας θέτουμε την μεταβλητή torinos->leksi σε true αφού
        // έχουμε λέξη.
        torinos->leksi = true;
    }

    // Τώρα πρέπει να ψάξουμε για κάθε μια απο τις Q λέξεις που θα μας δωθούν αν
    // υπάρχει στο Trie.
    int q;
    cin >> q;

    for (int i = 0; i < q; i++) {

        // Διαβάζουμε την συμβολοσειρά
        string x;
        cin >> x;

        // Ακολουθούμε γράμμα προς γράμμα και ελέγχουμε αν η συμβολοσειρά είναι
        // τελικά μέσα στο λεξικό μας ή όχι. Πάλι ξεκινούμε απο την ρίζα του
        // Trie (i.e. το start)
        node *torinos = &start;
        bool no_node = false;
        for (size_t j = 0; j < x.size(); j++) {

            if (torinos->p[x[j] - 'a'] == NULL) {
                // αν δεν υπάρχει η ακμή τότε σίγουρα η λέξη δεν είναι στο
                // λεξικό
                no_node = 1;
                break;
            } else {
                // διαφορετικά απλά ακολουθούμε την ακμή
                torinos = torinos->p[x[j] - 'a'];
            }
        }

        // αν δεν βρήκαμε κόμβο ή ο κόμβος δεν είναι κόμβος λέξης τότε τυπώνουμε
        // ΝΟ
        if (no_node || !(torinos->leksi)) {
            cout << "NO" << endl;
            continue;
        }

        // διαφορετικά αφου ακολουθήσαμε το μονοπάτι που ορίζεται απο τα
        // γράμματα της συμβολοσειράς που ψάχνουμε και φτάσαμε σε κόμβο όπου
        // έχουμε σημειώσει ότι κάποια λέξη τελείωνει σε αυτόν τότε ξέρουμε ότι
        // η συμβολοσειρά είναι μέσα στο λεξικό μας.
        cout << "YES" << endl;
    }
}