#include <iostream>
#include <string>
#include <algorithm>
#define rep(i, n) for(i = 0; i < (n); i++)
using namespace std;

int n;
string s;
bool dp[1024][1025];	//dp[S][T] = (ロック資源の集合がS, 待ち資源の集合がT)になることがあるか

bool solve() {
	int i, j, k, l;
	
	int locked = 0, waiting = 0;
	rep(i, s.length()) {
		if ('0' <= s[i] && s[i] <= '9') {
			waiting = s[i] - '0';
			dp[locked][1 << waiting] = true;
			locked = locked + (1 << waiting);
		}
		else {
			waiting = 10;	//T is covered Sが常に偽になる設定.
			dp[locked][1 << waiting] = true;
			locked = 0;
		}
	}
	
	rep(i, 1024) {
		//iと排反なnumberを探す.
		int digit[10]; int dNum = 0;
		rep(k, 10) {
			if ((i >> k) % 2 == 0) {
				digit[dNum++] = k;
			}
		}
		
		rep(j, 1025) {
			if (!dp[i][j]) continue;
			if ((i | j) == i) return true;
			
			rep(k, 1<<dNum) {
				int locked = 0;
				rep(l, dNum) if ((k >> l) & 1) locked += (1 << digit[l]);
				//waiting = 2^0, 2^1, …, 2^10
				rep(l, 11) {
					if (dp[locked][1 << l]) {
						dp[i | locked][min(1024, j | (1 << l))] = true;	//範囲外参照に注意！
					}
				}
			}
		}
	}
	
	rep(i, 1024) {
		rep(j, 1025) {
			if (!dp[i][j]) continue;
			if ((i | j) == i) return true;	//UNSAFE
		}
	}
	return false;
}

int main() {
	int i, j;
	
	while (cin >> n) {
		if (!n) break;
		cin >> s;
		rep(i, 1024) rep(j, 1025) dp[i][j] = false;
		bool res = solve();
		if (res) cout << "UNSAFE" << endl;
		else cout << "SAFE" << endl;
	}
	return 0;
}

//ポイント：
//・2段がまえの論理。
//・「全体集合から、ありえる状態集合を抽出☆」→「ありえる状態集合から、デッドロックが起きるものを抽出★」
//・「初期状態から「ある状態」に至れるか」の判定を直接的にできるのが、この問題のポイント。
//・「初期状態から「デッドロックが起きる状態」に至れるか」を直接判定しようとすると、再帰を考えることになり、このアプローチでは高速化を大して望めない。
//
//自分用解説：
//「各kについて、スレッドkで命令がa_k個処理された状態」になりえるかは、容易に求まる。（現在ロックしている資源がかぶっていないかを判定するだけ）
//なりえる状態のうち、(次確保したい資源の集合)が(ロックした資源の集合)の部分集合になるものでは、デッドロックが起きる。
//そういうものがない場合は、デッドロックは決して起きない。
//
//{a_1,…,a_10}をすべて試すとO(10000^10)くらいになり間に合わない。a_1, a_2, …の順で決めていくと考えると、わざわざ{a_k}を直接持たなくて良いことに気付く。
//なぜかというと、可能な{a_[i+1], …, a_10}の組み合わせは、
//・現在i個のスレッドのカーソルを確定させた。現在ロックしている資源はS
//という情報から定まり、
//デッドロックの判定は、この情報に
//・確保したい資源はT
//という情報を追加すればできるからである。よって、{a}の情報を持たずに探索ができる。(典型的なDP
//さらに、もう少し考えるとT - S(次確保「できる」資源の集合)を保存することでもデッドロックの判定ができるので、
//・i, S, T - S
//を使ってDPできることが分かる。
//
//また、この探索において、a_iの値を決めることは、「スレッドiでロックしている資源s_i, ロックしたい資源t_iを確定させる」
//ことに他ならないため、a_iを試す代わりに(s_i, t_i)組を試してもよい。
//しかし今回は文字列の長さがそこまで長くないため、直接a_iの値を試せばよい。(10000 < 2^10 * 10なので、上記の工夫はあまり意味がない)
//
//計算量は、(S, T - S)の組み合わせが3^10通り, iが11通り, 遷移が高々n(<=10000)通りより、3^10 * 11 * nくらいになる。
//…あれ、間に合わない。
//
//
//自分用解説2:
//デッドロックは、次欲しい資源がすべてロックされていることである。
//ロックされている資源の集合をS, 次欲しい資源の集合をTとして、ありえる(S, T)の組み合わせを列挙することを考える。(☆)
//1スレッドのとき、ありえる(S, T)の組み合わせは簡単に求まる。その集合を{(s_i, t_i), 1≦i≦hoge}とおく。
//2スレッドのときは、すでにロックしている資源をロックしなければよく、命令列も全スレッド共通なので、
//・(s_i or s_j, t_i or t_j) (s_i and s_j = empty)
//を列挙すればいいことに気付く。
//3スレッドのときも同様。
//今回の問題は、資源の個数≦スレッドの数より、スレッドの数に限りがないと考えてよく、
//ありえる(S, T)について、(S or s_i, T or t_i) (S and s_i = empty)を更新が止まるまでやればいいことが分かる。
//↑はS or s_iがSやs_iを含むことから、Sを2進数で表し、S=0,1,2,…の順で更新していけばいいことが分かる。
//
//一見, hoge=n, Sが2^10通り, Tが2^10通りで、2^20 * 10000 = 100億で間に合わないように見えるが、S and s_i = emptyとなるiだけを列挙すると、
//間に合う。計算量解析：1がk個立っているSはcomb[10][k]通り. そのSについて、S and s_i = emptyなるs_iは2^(10-k)通り. Tは2^10通り. t_iは高々11通り.
//Σcomb[10][k] * 2^(10 - k) = 3^10より、計算量は3^10 * 2^10 * 11 ≒ 6.6 * 10^8となる。(ダメやんけ)
//
//余談：
//☆で「スレッドのカーソルの位置」を列挙したくなりがちだが、デッドロックの条件から、(S, T)の組み合わせを列挙する方が後々筋が良い。
//「列挙するもの」は必ずしも「愚直な全探索」と同一にしなくても良いので、都合の良いものを列挙しよう。



Measured memory_bytes: 4080