## implementation

#include <algorithm>
#include <iostream>
#include <queue>
#include <tuple>
#include <vector>
#define repeat(i, n) for (int i = 0; (i) < int(n); ++(i))
#define whole(f, x, ...) ([&](decltype((x)) whole) { return (f)(begin(whole), end(whole), ## __VA_ARGS__); })(x)
using namespace std;
template <class T> inline void setmax(T & a, T const & b) { a = max(a, b); }
template <typename X, typename T> auto vectors(X x, T a) { return vector<T>(x, a); }
template <typename X, typename Y, typename Z, typename... Zs> auto vectors(X x, Y y, Z z, Zs... zs) { auto cont = vectors(y, z, zs...); return vector<decltype(cont)>(x, cont); }
template <class T> using reversed_priority_queue = priority_queue<T, vector<T>, greater<T> >;

bool is_prefix(string const & a, string const & b) {
return a.compare(0, b.length(), b);
}
int main() {
while (true) {
// input
int n, edges, start, goal; cin >> n >> edges >> start >> goal;
if (n == 0) break;
auto label = vectors(n, n, vector<string>());
vector<vector<int> > g(n);
int label_length_max = 0;
repeat (i, edges) {
int x, y; string s; cin >> x >> y >> s;
label[x][y].push_back(s);
setmax<int>(label_length_max, s.length());
g[x].push_back(y);
}
repeat (x, n) {
whole(sort, g[x]);
g[x].erase(whole(unique, g[x]), g[x].end());
}
// solve
string result;
vector<string> dist(n);
reversed_priority_queue<tuple<string, int, int> > que;
dist[start] = "";
que.emplace(dist[start], -1, start);
while (not que.empty()) {
string dist_x; int z, x; tie(dist_x, z, x) = que.top(); que.pop();
if (label_length_max * (n + 3) < dist[x].length()) continue;
if (z != -1 and dist[x] + "\xff" < dist_x) continue;
if (z != -1 and not is_prefix(dist[x], dist_x)) continue;
if (x == goal) {
if (result.empty()) {
result = dist_x;
} else if (dist_x < result) {
result = "NO";
break;
} else {
continue;
}
}
dist[x] = dist_x;
for (int y : g[x]) {
for (string delta : label[x][y]) {
if (dist[x] + delta < dist[y] + "\xff") {
que.emplace(dist[x] + delta, x, y);
}
}
}
}
// output
if (dist[goal].empty() or label_length_max * n < dist[goal].length()) {
dist[goal] = "NO";
}
cout << dist[goal] << endl;
}
return 0;
}