Problem:
Deadlock Detection

In concurrent processing environments, a deadlock is an undesirable
situation where two or more threads are mutually waiting for others to
finish using some resources and cannot proceed further.

Your task is to detect whether there is any possibility of deadlocks
when multiple threads try to execute a given instruction sequence concurrently.

The instruction sequence consists of characters 'u' or digits from
'0' to '9', and each of them represents one instruction.
10 threads are trying to execute the same single instruction sequence.
Each thread starts its execution from the beginning of the sequence and
continues in the given order, until all the instructions are
executed.

There are 10 shared resources called locks from L0 to L9.
A digit k is the instruction for acquiring the lock Lk.
After one of the threads acquires a lock Lk,
it is kept by the thread until it is released by the instruction 'u'.
While a lock is kept, none of the threads, including one already acquired it,
cannot newly acquire the same lock Lk.

Precisely speaking, the following steps are repeated until all threads
finish.

One thread that has not finished yet is chosen arbitrarily.

The chosen thread tries to execute the next instruction that is not executed yet.

If the next instruction is a digit k and
the lock Lk is not kept by any thread,
the thread executes the instruction k and acquires Lk.

If the next instruction is a digit k and
the lock Lk is already kept by some thread,
the instruction k is not executed.

If the next instruction is 'u',
the instruction is executed and all the locks currently kept by the thread
are released.

After executing several steps, sometimes, it falls into the situation
that the next instructions
of all unfinished threads are for acquiring already kept locks.
Once such a situation happens, no instruction will ever be executed no
matter which thread is chosen. This situation is called a deadlock.

There are instruction sequences for which threads can never reach a
deadlock regardless of the execution order.
Such instruction sequences are called safe.
Otherwise, in other words, if there exists one or more execution orders
that lead to a deadlock, the execution sequence is called unsafe.

Your task is to write a program that tells whether the given instruction sequence is safe or unsafe.

Input

The input consists of at most 50 datasets, each in the following format.

n

s

n is the length of the instruction sequence and s is a string representing the sequence.
n is a positive integer not exceeding 10,000.
Each character of s is either a digit ('0' to '9') or 'u',
and s always ends with 'u'.

The end of the input is indicated by a line with a zero.

Output

For each dataset, if the given instruction sequence is safe, then print "SAFE" in a line.
If it is unsafe, then print "UNSAFE" in a line.

Sample Input

11
01u12u0123u
6
01u10u
8
201u210u
9
01u12u20u
3
77u
12
9u8u845u954u
0

Output for the Sample Input

SAFE
UNSAFE
SAFE
UNSAFE
UNSAFE
UNSAFE

The second input "01u10u" may possibly cause a deadlock. After one
thread has executed the initial four instructions "01u1", the
thread keeps only one lock L1. If another thread executes the first
instruction '0' at this time, this thread acquires L0. Then, the
first thread tries to acquire L0, already kept by the second
thread, while the second thread tries to acquire L1, kept by the
first thread; This leads to a deadlock.

→
→

Figure 1: Why the Sample Input 2 "01u10u" is unsafe.

Contrarily, the third input "201u210u" is safe.
If one thread had executed up to "201u21" and another to "20", then
one may think it would lead to a deadlock, but this
can never happen because no two threads can simultaneously keep L2.

Solution in C# (judge verdict: Accepted)

using System;
using System.Linq;
using System.Collections.Generic;
using Debug = System.Diagnostics.Debug;
using SB = System.Text.StringBuilder;
using System.Numerics;
using static System.Math;
using P = System.Numerics.Complex;
namespace Program {
    public class Solver {
        Random rnd = new Random(0);
        public void Solve() {
            for (; ; )
            {
                var n = ri;
                if (n == 0) break;
                var s = rs;

                var die = false;
                var next = new int[1 << 10];
                const int ALL = 1023;
                next[0] = ALL;
                {
                    var mask = 0;
                    for (int i = 0; i < n; i++)
                    {
                        if (s[i] == 'u') mask = 0;
                        else
                        {
                            var v = 1 << (s[i] - '0');
                            next[mask] |= v;
                            die |= (mask & v) > 0;
                            mask |= v;
                        }
                    }
                }
                var vis = new bool[1 << 10, 10];
                Action<int, int> dfs = null;
                dfs = (used, nex) =>
                {
                    if (vis[used, nex]) return;
                    vis[used, nex] = true;
                    var rem = ALL ^ used;
                    var need = 1 << nex;
                    for (int sub = rem; sub > 0; sub = ((sub - 1) & rem))
                    {
                        if (next[sub] == 0) continue;
                        if ((sub & need) > 0)
                        {
                            for (int d = 0; d < 10; d++)
                                if ((next[sub] >> d & 1) == 1)
                                {
                                    if ((used >> d & 1) == 1) die |= true;
                                    else dfs(sub | used, d);
                                }
                        }
                    }
                };
                for (int i = 0; i < 10; i++)
                    dfs(0, i);
                Console.WriteLine(die ? "UNSAFE" : "SAFE");
            }

        }
        const long INF = 1L << 60;
        int ri { get { return sc.Integer(); } }
        long rl { get { return sc.Long(); } }
        double rd { get { return sc.Double(); } }
        string rs { get { return sc.Scan(); } }
        public IO.StreamScanner sc = new IO.StreamScanner(Console.OpenStandardInput());

        static T[] Enumerate<T>(int n, Func<int, T> f) {
            var a = new T[n];
            for (int i = 0; i < n; ++i) a[i] = f(i);
            return a;
        }
        static public void Swap<T>(ref T a, ref T b) { var tmp = a; a = b; b = tmp; }
    }
}

#region main
static class Ex {
    static public string AsString(this IEnumerable<char> ie) { return new string(ie.ToArray()); }
    static public string AsJoinedString<T>(this IEnumerable<T> ie, string st = " ") {
        return string.Join(st, ie);
    }
    static public void Main() {
        Console.SetOut(new Program.IO.Printer(Console.OpenStandardOutput()) { AutoFlush = false });
        var solver = new Program.Solver();
        solver.Solve();
        Console.Out.Flush();
    }
}
#endregion
#region Ex
namespace Program.IO {
    using System.IO;
    using System.Text;
    using System.Globalization;

    public class Printer: StreamWriter {
        public override IFormatProvider FormatProvider { get { return CultureInfo.InvariantCulture; } }
        public Printer(Stream stream) : base(stream, new UTF8Encoding(false, true)) { }
    }

    public class StreamScanner {
        public StreamScanner(Stream stream) { str = stream; }

        public readonly Stream str;
        private readonly byte[] buf = new byte[1024];
        private int len, ptr;
        public bool isEof = false;
        public bool IsEndOfStream { get { return isEof; } }

        private byte read() {
            if (isEof) return 0;
            if (ptr >= len)
            {
                ptr = 0;
                if ((len = str.Read(buf, 0, 1024)) <= 0)
                {
                    isEof = true;
                    return 0;
                }
            }
            return buf[ptr++];
        }

        public char Char() {
            byte b = 0;
            do b = read(); while ((b < 33 || 126 < b) && !isEof);
            return (char)b;
        }
        public string Scan() {
            var sb = new StringBuilder();
            for (var b = Char(); b >= 33 && b <= 126; b = (char)read()) sb.Append(b);
            return sb.ToString();
        }
        public string ScanLine() {
            var sb = new StringBuilder();
            for (var b = Char(); b != '\n' && b != 0; b = (char)read()) if (b != '\r') sb.Append(b);
            return sb.ToString();
        }
        public long Long() { return isEof ? long.MinValue : long.Parse(Scan()); }
        public int Integer() { return isEof ? int.MinValue : int.Parse(Scan()); }
        public double Double() { return isEof ? double.NaN : double.Parse(Scan(), CultureInfo.InvariantCulture); }
    }
}

#endregion