#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<string>
#include<stack>
#include<cstdio>
#include<cmath>
using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<ll,ll> P;
typedef pair<ll,P> P1;

#define fr first
#define sc second
#define mp make_pair
#define pb push_back
#define rep(i,x) for(int i=0;i<x;i++)
#define rep1(i,x) for(int i=1;i<=x;i++)
#define rrep(i,x) for(int i=x-1;i>=0;i--)
#define rrep1(i,x) for(int i=x;i>0;i--)
#define sor(v) sort(v.begin(),v.end())
#define rev(s) reverse(s.begin(),s.end())
#define lb(vec,a) lower_bound(vec.begin(),vec.end(),a)
#define ub(vec,a) upper_bound(vec.begin(),vec.end(),a)
#define uniq(vec) vec.erase(unique(vec.begin(),vec.end()),vec.end())
#define mp1(a,b,c) P1(a,P(b,c))

const int INF=1000000000;
const int dir_4[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
const int dir_8[8][2]={{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}};

const ll M = 1000000007;

struct sstring{
	string s;
	bool operator < (sstring y) const {
		rep(i,min(this->s.size(),y.s.size())){
			if(this->s[i] < y.s[i])return true;
			else if(this->s[i] > y.s[i])return false;
		}
		return this->s.size() < y.s.size();
	}
};
sstring ms(string s){
	sstring ret; ret.s = s; return ret;
}

int f(string x,string y){
	int s = min ( x.size() , y.size() );
	rep(i,s){
		if(x[i] < y[i])return -1;
		if(x[i] > y[i])return 1;
	}
	if(x.size() < y.size())return 1;
	else return 0;
}

int main(){
	while(1){
		int n,a,s,g;
		int x[402],y[402];
		string lab[402];
		
		scanf("%d%d%d%d",&n,&a,&s,&g); if(n == 0)break;
		rep(i,a){
			scanf("%d%d",&x[i],&y[i]);
			cin >> lab[i];
		}
		/*puts("----------------------------");
		printf("%d %d %d %d\n",n,a,s,g);
		rep(i,a){
			printf("%d %d ",x[i],y[i]);
			cout << lab[i] << endl;
		}*/
		
		vector<pair<int,string>> G[42];
		vector<int> rG[42];
		rep(i,a){
			G[x[i]].pb(pair<int,string>(y[i],lab[i]));
			rG[y[i]].pb(x[i]);
		}
		bool c[42]; rep(i,42)c[i] = false;
		queue<int> que_;
		c[g] = true;
		que_.push(g);
		while(!que_.empty()){
			int p = que_.front(); que_.pop();
			//cout << p << ":";
			rep(i,rG[p].size()){
				if(!c[rG[p][i]]){
					//cout << rG[p][i] << " ";
					c[rG[p][i]] = true;
					que_.push(rG[p][i]);
				}
			}
			//cout << endl;
		}
		rep(i,n){
			string MIN;
			rep(j,G[i].size()){
				if((!c[i]) || (!c[G[i][j].fr]))continue;
				int t = f(MIN,G[i][j].sc);
				if(t == 1)MIN = G[i][j].sc;
			}
			vector<pair<int,string>>G_;
			rep(j,G[i].size()){
				if((!c[i]) || (!c[G[i][j].fr]))continue;
				if(f(MIN,G[i][j].sc) >= 0)G_.pb(G[i][j]);
			}
			G[i] = G_;
		}
		/*rep(i,n){
			printf("%d:%d\n",i,G[i].size());
			rep(j,G[i].size()){
				printf("%d ",G[i][j].fr);
				cout << G[i][j].sc << endl;
			}
		}*/
		
		string dist[42];
		bool used[42][252];
		rep(i,42){
			rep(j,252)used[i][j] = false;
			dist[i] = "";
			dist[i] += '~';
		}
		priority_queue<pair<sstring,int>,vector<pair<sstring,int>>,greater<pair<sstring,int>>> que;
		que.push(pair<sstring,int>(ms(""),s));
		
		while(!que.empty()){
			pair<sstring,int> p = que.top(); que.pop();
			//cout << p.fr.s << " " << p.sc << endl;
			int t = f(dist[p.sc],p.fr.s);
			if(t == -1)continue;
			else if(t == 1){
				for(int i = p.fr.s.size() ; i <= dist[p.sc].size() ; i ++){
					used[p.sc][i] = false;
				}
				dist[p.sc] = p.fr.s;
			}
			if(used[p.sc][p.fr.s.size()])continue;
			used[p.sc][p.fr.s.size()] = true;
			if(p.sc == g){
				cout << p.fr.s << endl;
				goto Next;
			}
			if(p.fr.s.size() > 240){
				break;
			}
			rep(i,G[p.sc].size()){
				pair<int,string> ed = G[p.sc][i];
				if(f(dist[ed.fr],p.fr.s+ed.sc) != -1)que.push(pair<sstring,int>(ms(p.fr.s+ed.sc),ed.fr));
			}
		}
		cout << "NO" << endl;
		Next:;
		
		/*dist[s] = "";
		rep(i,n){
			int v = -1;
			rep(j,n){
				if(used[j])continue;
				if(v == -1 || lesser(dist[j],dist[v])){
					v = j;
				}
			}
			if(v == -1)break;
			used[v] = true;
			rep(j,G[v].size()){
				pair<int,string> ed = G[v][j];
				if(lesser(dist[v]+ed.sc,dist[ed.fr])){
					dist[ed.fr] = dist[v]+ed.sc;
				}
			}
		}
		ret = dist[g];
		if(ret == "~"){
			puts("NO");
			continue;
		}
		
		bool c[42];
		rep(i,42)c[i] = false;
		c[g] = true;
		queue<int> que; que.push(g);
		while(!que.empty()){
			int q = que.front(); que.pop();
			rep(i,rG[q].size()){
				if(c[rG[q][i]])continue;
				c[rG[q][i]] = true;
				que.push(c[rG[q][i]]);
			}
		}
		
		bool t = true;
		bool used_[42][242];
		rep(i,42)rep(j,242)used_[i][j] = false;
		priority_queue<P,vector<P>,greater<P>> que_;
		rep(i,42){
			if(c[i])que_.push(P(dist[i].size(),i));
		}
		while(!que_.empty()){
			P p = que_.top(); que_.pop();
			if(used_[p.sc][p.fr])continue;
			used_[p.sc][p.fr] = true;
			rep(i,G[p.sc].size()){
				pair<int,string> ed = G[p.sc][i];
				int par = f(p.fr,ed.sc);
				if(par == 1){
					t = false;
					break;
				}
				else if(par == 0){
					if(p.fr+ed.sc.size() < ret.size()){
						que_.push(P(p.fr+ed.sc.size(),ed.fr));
					}
				}
			}
		}
		
		if(t)cout << ret << endl;
		else cout << "NO" << endl;*/
	}
}

Measured memory_bytes: 1560