Problem:
Score: 400 points

Problem Statement

AtCoder Inc. has decided to lock the door of its office with a 3-digit PIN code.

The company has an N-digit lucky number, S. Takahashi, the president, will erase N-3 digits from S and concatenate the remaining 3 digits without changing the order to set the PIN code.

How many different PIN codes can he set this way?

Both the lucky number and the PIN code may begin with a 0.

Constraints

4 \leq N \leq 30000

S is a string of length N consisting of digits.

Input

Input is given from Standard Input in the following format:

N
S

Output

Print the number of different PIN codes Takahashi can set.

Sample Input 1

4
0224

Sample Output 1

3

Takahashi has the following options:

Erase the first digit of S and set 224.

Erase the second digit of S and set 024.

Erase the third digit of S and set 024.

Erase the fourth digit of S and set 022.

Thus, he can set three different PIN codes: 022, 024, and 224.

Sample Input 2

6
123123

Sample Output 2

17

Sample Input 3

19
3141592653589793238

Sample Output 3

329

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public class MainClass
{
	public const long Giri = 1000000007;

	public static void Main(string[] args)
	{
		Console.ReadLine();
		var c = Console.ReadLine();
		var chars = c.ToCharArray();
		var size = new List<int>[10];
		for(int i = 0; i < c.Length; i++)
		{
			if(size[c[i] - '0']==null)
			{
				size[c[i] - '0'] = new List<int>();
			}
			size[c[i] - '0'].Add(i);
		}
		
		var indexes = new int[10][];
		for(int i = 0; i < 10; i++)
		{
			if(size[i]==null)
			{
				indexes[i] = new int[0];
				continue;
			}
			indexes[i] = size[i].ToArray();
		}
		long wa = 0;
		for(int i = 0; i <= 9; i++)
		{
			if(indexes[i].Length == 0)
				continue;
			int ii = indexes[i][0];
			for(int j = 0; j <= 9; j++)
			{
				int ij = UpperBound(indexes[j], ii);
				if(ij >= indexes[j].Length)
					continue;
				ij = indexes[j][ij];
				if(ii >= ij)
					continue;
				for(int k = 0; k <= 9; k++)
				{
					int ik = UpperBound(indexes[k], ij);
					if(ik >= indexes[k].Length )
						continue;
					ik = indexes[k][ik];
					wa++;
				}
			}
		}
		Console.WriteLine(wa);
	}


public static int UpperBound<T>(T[] arr, int start, int end, T value, IComparer<T> comparer)
{
    int low = start;
    int high = end;
    int mid;
    while (low < high)
    {
        mid = ((high - low) >> 1) + low;
        if (comparer.Compare(arr[mid], value) <= 0)
            low = mid + 1;
        else
            high = mid;
    }
    return low;
}

//引数省略のオーバーロード
public static int UpperBound<T>(T[] arr, T value)
{
    return UpperBound(arr, 0, arr.Length, value, Comparer<T>.Default);
}

	static long GCD(long a, long b)
	{
		var v = new[] { a, b };
		while (v[1] != 0) { v = new[] { v[1], v[0] % v[1] }; }
		return v[0];
	}
	public static string[] Input() { return Console.ReadLine().Split(' '); }
	public static int Clamp(int val, int max, int min = 0) { return Math.Min(max, Math.Max(min, val)); }
}

public static class Smarter
{
	public static int ToInt(this string str) { return int.Parse(str); }
	public static long ToLong(this string str) { return long.Parse(str); }
	public static int[] ToInt(this string[] str)
	{
		var r = new int[str.Length];
		for(int i = 0; i < str.Length; i++) r[i] = str[i].ToInt();
		return r;
	 }
}