#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <cfloat>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <iostream>
#include <set>
#include <map>
#include <time.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;

#define NUM 40
#define MAX_LEN 250

void strcpy(char* to,char* str){
	to[0] = '\0';
	for(int i=0;str[i] != '\0';i++){
		to[i] = str[i];
		to[i+1] = '\0';
	}
}

void strcpy2(char to[300],const char str[300]){
	to[0] = '\0';
	for(int i=0;str[i] != '\0';i++){
		to[i] = str[i];
		to[i+1] = '\0';
	}
}

void addcpy(char to[300],const char str[300]){
	int index,second_index;
	for(index = 0; to[index] != '\0';index++);
	//printf("index:%d\n",index);
	for(second_index = 0; str[second_index] != '\0';second_index++){
		to[index+second_index] = str[second_index];
	}
	to[index+second_index] = '\0';
}

bool strCmp(const char base[300],const char comp[300]){
	int length1,length2;
	for(length1=0;base[length1] != '\0';length1++);
	for(length2=0;comp[length2] != '\0';length2++);
	if(length1 != length2)return false;

	for(int i=0;base[i] != '\0'; i++){
		if(base[i] != comp[i])return false;
	}
	return true;
}



//辞書順が等しい時は3,leftの方が早い場合は1,rightの方が早いときは2
int strCmp2(const char left[300],const char right[300]){
	int i;

	if(strCmp(left,right))return 3;

	for(i=0;left[i] != '\0' && right[i] != '\0'; i++){
		if(left[i] != right[i]){
			if(left[i] < right[i])return 1;
			else{
				return 2;
			}
		}
	}
	if(left[i] == '\0')return 1;
	else{
		return 2;
	}
}

struct Edge{
	Edge(int arg_to,char arg_buf[7]){
		to = arg_to;
		strcpy(buf,arg_buf);
		deleted = false;
	}
	int to;
	char buf[7];
	bool deleted;
};

struct Data{
	Data(int arg_node_id,int arg_edge_id){
		node_id  = arg_node_id;
		edge_id = arg_edge_id;
	}
	int node_id;
	int edge_id;
};

struct Info{
	Info(int arg_node_id,char arg_tmp_string[300]){
		node_id = arg_node_id;
		strcpy(tmp_string,arg_tmp_string);
	}

	Info(){
		node_id = 0;
	}

	bool operator<(const struct Info &arg) const{
		int compare = strCmp2(tmp_string,arg.tmp_string);

		if(compare == 2 || compare == 3){ //argの方が早い
			return true; //PQなので逆
		}else{
			return false;
		}
	}
	int node_id;
	char tmp_string[300];
};


int N,E,start,goal;
char min_string[40][251][300]; //min_string[node_id][length][string]
bool reachable[NUM][NUM];
vector<Edge> G[NUM];


void func(){

	for(int i = 0; i < N; i++)G[i].clear();

	int from,to;
	char buf[7];

	for(int i = 0; i < N; i++){
		for(int k = 0; k < N; k++){
			if(i != k){
				reachable[i][k] = false;
			}else{
				reachable[i][k] = true;
			}
		}
	}

	for(int i = 0; i < E; i++){
		scanf("%d %d %s",&from,&to,buf);
		reachable[from][to] = true;
		G[from].push_back(Edge(to,buf));
	}

    for(int mid = 0;mid < N; mid++){
    	for(int st = 0; st < N; st++){
    		if(!reachable[st][mid])continue;
    		for(int gl = 0; gl < N; gl++){
    			if(!reachable[mid][gl])continue;
    			reachable[st][gl] = true;
    		}
    	}
	}

    if(!reachable[start][goal]){
    	printf("NO\n");
    	return;
    }

	int compare;

	//エッジを削る(同じノードかつ同じ行先)
	for(int i = 0; i < N; i++){
		for(int k = 0; k+1 < G[i].size(); k++){
			if(G[i][k].deleted)continue;
			for(int p = k+1; p < G[i].size(); p++){
				if(G[i][k].to != G[i][p].to)continue;
				if(G[i][k].buf[0] == G[i][p].buf[0])continue; //片方がもう片方に含まれる場合はSKIP

				compare = strCmp2(G[i][k].buf,G[i][p].buf);

				if(compare == 1){
					G[i][p].deleted = true;
				}else{
					G[i][k].deleted = true;
				}
			}
		}
	}

	//bool check[N];

	//長い文字列を記録
	for(int i = 0; i < N; i++){
		//check[i] = false; //閉路に属しているか
		for(int length = 0; length < MAX_LEN; length++){
			for(int k = 0; k <= length; k++){
				min_string[i][length][k] = 'z';
			}
			min_string[i][length][length+1] = '\0';
		}
	}

	min_string[start][0][0] = '\0';

	priority_queue<Info> Q;
	char first_string[300];
	first_string[0] = '\0';

	Q.push(Info(start,first_string));
	int next_node,next_length;
	char ans[300],work[300];

	//int debug = 0;

	int length;

	while(!Q.empty()){

		for(length = 0; Q.top().tmp_string[length] != '\0'; length++);

		if(length >= 6*N){
			printf("NO\n");
			return;
		}

		if(Q.top().node_id == goal){

			printf("%s\n",Q.top().tmp_string);
			return;
		}else if(strCmp2(Q.top().tmp_string,min_string[Q.top().node_id][length]) == 2){
			Q.pop();
		}else{

			for(int i = 0; i < G[Q.top().node_id].size(); i++){
				//if(G[Q.top().node_id][i].deleted)continue;
				next_node = G[Q.top().node_id][i].to;
				//if(next_node == Q.top().node_id || reachable[next_node][goal] == false)continue; //自己ループ&ゴールに着けないノードは無視
				if(reachable[next_node][goal] == false)continue;

				//新しい文字列を作る
				work[0] = '\0';
				strcpy2(work,Q.top().tmp_string);
				//printf("work:%s tmp_str:%s\n",work,Q.top().tmp_string);
				addcpy(work,G[Q.top().node_id][i].buf);
				//printf("work:%s buf:%s\n",work,G[Q.top().node_id][i].buf);

				for(next_length = 0; work[next_length] != '\0'; next_length++);

				if(strCmp2(work,min_string[next_node][next_length]) != 1)continue;
				strcpy2(min_string[next_node][next_length],work);

				Info next_info;
				next_info.node_id = next_node;
				strcpy(next_info.tmp_string,work);

				Q.push(next_info);
			}
			Q.pop();
		}
	}

	printf("NO\n");
	return;

}


int main(){

	while(true){
		scanf("%d %d %d %d",&N,&E,&start,&goal);
		if(N == 0 && E == 0 && start == 0 && goal == 0)break;

		func();
	}

	return 0;
}



Measured memory_bytes: 7184