#include <algorithm>
#include <iostream>
#include <list>
#include <vector>
#include <set>
#include <string>

const std::string empty;
const std::string no("NO");

typedef int node_t;

struct arrow_t {
    node_t source;
    node_t sink;
    std::string label;
};

struct substring_t {
    std::string::const_iterator begin;
    std::string::const_iterator end;
    substring_t(const std::string& s) : begin(s.begin()), end(s.end()) {}
    int size() const { return end - begin; }
};

struct entry_t {
    substring_t label;
    node_t node;
    entry_t(const substring_t& label, node_t node) : label(label), node(node) {}
};

typedef std::list<entry_t> queue_t;

int compare(const substring_t& s1, const substring_t& s2) {
    for (std::string::const_iterator i1 = s1.begin, i2 = s2.begin; ; ++i1, ++i2) {
        if (i1 == s1.end) {
            return i2 == s2.end? 0 : -1;
        } else if (i2 == s2.end) {
            return 1;
        }
        if (*i1 < *i2) {
            return -2;
        } else if (*i1 > *i2) {
            return 2;
        }
    }
}

int compare(const entry_t& s1, const entry_t& s2) {
    int c = compare(s1.label, s2.label);
    if (c != 0) {
        return c;
    } else if (s1.node < s2.node) {
        return -1;
    } else if (s1.node > s2.node) {
        return 1;
    } else {
        return 0;
    }
}

queue_t::iterator insert(queue_t& queue, entry_t entry) {
    for (queue_t::iterator it = queue.begin(); it != queue.end(); ++it) {
        switch (compare(*it, entry)) {
        case -2:
            return queue.end();
        case -1:
            entry.label.begin += it->label.size();
            continue;
        case 0:
            return it;
        case 1:
            it->label.begin += entry.label.size();
            return queue.insert(it, entry);
        case 2:
            return queue.insert(queue.erase(it, queue.end()), entry);
        }
    }
    return queue.insert(queue.end(), entry);
}

std::set<node_t> collect(int n, const std::vector<arrow_t>& arrows, node_t goal) {
    std::vector<std::vector<node_t> > ins(n);
    for (const arrow_t& arrow : arrows) {
        ins[arrow.sink].push_back(arrow.source);
    }

    std::set<node_t> result;
    std::vector<node_t> stack;
    stack.push_back(goal);
    while (!stack.empty()) {
        node_t node = stack.back();
        stack.pop_back();

        result.insert(node);

        for (int source : ins[node]) {
            if (result.find(source) == result.end()) {
                stack.push_back(source);
            }
        }
    }
    return result;
}

std::string calc(int n, const std::vector<arrow_t>& arrows, node_t start, node_t goal) {
    const std::set<node_t>& reachable_set = collect(n, arrows, goal);
    if (reachable_set.find(start) == reachable_set.end()) {
        return no;
    }

    size_t max = 0;
    std::vector<std::vector<entry_t> > outs(n);
    for (const arrow_t& arrow : arrows) {
        if (reachable_set.find(arrow.sink) != reachable_set.end()) {
            max = std::max(max, arrow.label.size());
            outs[arrow.source].push_back(entry_t(arrow.label, arrow.sink));
        }
    }

    size_t limit = n * max;

    std::string result;
    queue_t queue;
    queue.push_back(entry_t(empty, start));
    while (true) {
        substring_t label = queue.front().label;
        int node = queue.front().node;
        queue.pop_front();

        result.append(label.begin, label.end);
        if (node == goal) {
            return result;
        }
        if (result.size() > limit) {
            return no;
        }

        for (const entry_t& entry : outs[node]) {
            insert(queue, entry);
        }
    }
}

int main() {
    while (true) {
        int n, a, s, g;
        std::cin >> n >> a >> s >> g;
        if (n == 0) {
            break;
        }
        std::vector<arrow_t> arrows(a);
        for (arrow_t& arrow : arrows) {
            std::cin >> arrow.source >> arrow.sink >> arrow.label;
        }
        std::cout << calc(n, arrows, s, g) << std::endl;
    }
    return 0;
}