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

int bitcount(unsigned long long v) {
    v = v - ((v >> 1) & 0x5555555555555555ULL);
    v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL);
    v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
    v = v + (v >> 8);
    v = v + (v >> 16);
    v = v + (v >> 32);
    return int(v) & 0x3f;
}

typedef int node_t;
struct nodeset_t {
    unsigned long long value;
    nodeset_t() : value(0ULL) {}
    void insert(node_t node) { value |= (1ULL << node); }
    void insert(const nodeset_t& nodes) { value |= nodes.value; }
    bool contains(node_t node) const { return value & (1ULL << node); }
    bool empty() const { return !value; }
    node_t front() { return bitcount((value & -value) - 1); }
    void pop_front() { value &= value - 1; }
};
 
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()) {}
    inline int size() const { return end - begin; }

    inline std::string to_string() const { return std::string(begin, end); }
};

struct entry_t {
    substring_t label;
    nodeset_t nodes;
    entry_t(const substring_t& label) : label(label) {}
};
 
typedef std::list<entry_t> queue_t;

const std::string empty;
const std::string no("NO");
 
int compare(const substring_t& s1, const substring_t& s2, int skip) {
    for (std::string::const_iterator i1 = s1.begin + skip, i2 = s2.begin + skip; ; ++i1, ++i2) {
        if (i1 == s1.end) {
            return i2 == s2.end ? 0 : -1;
        } else if (i2 == s2.end) {
            return 1;
        }
        int c = ((int) *i1) - ((int) *i2);
        if (c != 0) {
            return c < 0 ? -2 : 2;
        }
    }
    return 0;
}
 
queue_t::iterator insert(queue_t& queue, substring_t label) {
    int skip = 0;
    for (queue_t::iterator it = queue.begin(); it != queue.end(); it++) {
        switch (compare(it->label, label, skip)) {
        case -2:
            return queue.end();
        case -1:
            skip = it->label.size();
            continue;
        case 0:
            return it;
        case 1:
            return queue.insert(it, entry_t(label));
        case 2:
            return queue.insert(queue.erase(it, queue.end()), entry_t(label));
        }
    }
    return queue.insert(queue.end(), entry_t(label));
}

queue_t::iterator insert(queue_t& queue, const substring_t& label, node_t node) {
    queue_t::iterator it = insert(queue, label);
    if (it != queue.end()) {
        it->nodes.insert(node);
    }
    return it;
}

queue_t::iterator insert(queue_t& queue, const substring_t& label, const nodeset_t& nodes) {
    queue_t::iterator it = insert(queue, label);
    if (it != queue.end()) {
        it->nodes.insert(nodes);
    }
    return it;
}
 
nodeset_t collect(int n, const std::vector<arrow_t>& arrows, node_t goal) {
    std::vector<nodeset_t> ins(n);
    for (const arrow_t& arrow : arrows) {
        ins[arrow.sink].insert(arrow.source);
    }
 
    nodeset_t result;
    nodeset_t stack;
    stack.insert(goal);
    while (!stack.empty()) {
        node_t node = stack.front();
        stack.pop_front();
 
        result.insert(node);
 
        nodeset_t temp = ins[node];
        while (!temp.empty()) {
            node_t source = temp.front();
            temp.pop_front();
            if (!result.contains(source)) {
                stack.insert(source);
            }
        }
    }
    return result;
}
 
std::string calc(int n, const std::vector<arrow_t>& arrows, node_t start, node_t goal) {
    const nodeset_t& reachable_set = collect(n, arrows, goal);
    if (!reachable_set.contains(start)) {
        return no;
    }
 
    size_t max = 0;
    std::vector<queue_t> outs(n);
    for (const arrow_t& arrow : arrows) {
        if (reachable_set.contains(arrow.sink)) {
            max = std::max(max, arrow.label.size());
            insert(outs[arrow.source], arrow.label, arrow.sink);
        }
    }
 
    size_t limit = n * max;
 
    std::string result;
    queue_t queue;
    queue.push_back(entry_t(empty));
    queue.front().nodes.insert(start);
    while (true) {
        substring_t label = queue.front().label;
        nodeset_t nodes = queue.front().nodes;
        queue.pop_front();
 
        result.append(label.begin, label.end);
        if (nodes.contains(goal)) {
            return result;
        }
        if (result.size() > limit) {
            return no;
        }

        int size = label.size();
        for (entry_t& entry : queue) {
            entry.label.begin += size;
        }

        nodeset_t temp = nodes;
        while (!temp.empty()) {
            node_t node = temp.front();
            temp.pop_front();
            for (const entry_t& entry : outs[node]) {
                insert(queue, entry.label, entry.nodes);
            }
        }
    }
}
 
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;
}

Measured memory_bytes: 3044