#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[300]){
        to = arg_to;
        strcpy(buf,arg_buf);
        deleted = false;
    }
    bool operator<(const struct Edge &arg) const{
            int compare = strCmp2(buf,arg.buf);

            if(compare == 2 || compare == 3){ //argの方が早い
                return false;
            }else{
                return true;
            }
        }
    int to;
    char buf[300];
    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[300];

    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; //閉路に属しているか
    	sort(G[i].begin(),G[i].end());
        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: 7552