Problem:
Score : 600 points

Problem Statement

You are given a string S of length 2N consisting of lowercase English letters.

There are 2^{2N} ways to color each character in S red or blue. Among these ways, how many satisfy the following condition?

The string obtained by reading the characters painted red from left to right is equal to the string obtained by reading the characters painted blue from right to left.

Constraints

1 \leq N \leq 18

The length of S is 2N.

S consists of lowercase English letters.

Input

Input is given from Standard Input in the following format:

N
S

Output

Print the number of ways to paint the string that satisfy the condition.

Sample Input 1

4
cabaacba

Sample Output 1

4

There are four ways to paint the string, as follows:

cabaacba

cabaacba

cabaacba

cabaacba

Sample Input 2

11
mippiisssisssiipsspiim

Sample Output 2

504

Sample Input 3

4
abcdefgh

Sample Output 3

0

Sample Input 4

18
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Sample Output 4

9075135300

The answer may not be representable as a 32-bit integer.

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

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

namespace AGC026_C
{
    class Program
    {
        static void Main(string[] args)
        {
            int n = int.Parse(Console.ReadLine());
            string s = Console.ReadLine();

            char[] upperHalf = new char[n];
            char[] lowerHalf = new char[n];

            char[] red = new char[n];
            char[] blue = new char[n];

            s.CopyTo(0, upperHalf, 0, n);



#if false
            HashSet<Tuple<char[], char[]>> hash = new HashSet<Tuple<char[], char[]>>();





            var hashTest = new HashSet<Tuple<string, string>>();

            string a = "a";
            string b = "b";
            string c = "c";
            string d = "d";

            hashTest.Add(new Tuple<string, string>(a, b));
            hashTest.Add(new Tuple<string, string>(c, d));

            hashTest.Clear();

            hashTest.Add(new Tuple<string, string>(a, b));
            a = "c";
            b = "d";
            hashTest.Add(new Tuple<string, string>(a, b));

            hashTest.Clear();

            a = "a";
            b = "b";
            hashTest.Add(new Tuple<string, string>(a, b));
            c = "a";
            d = "b";

            var t_ = new Tuple<string, string>(c, d);
            bool flag_ = hashTest.Contains(t_);

            hashTest.Add(t_);




            var hashTest2 = new HashSet<Tuple<char[], char[]>>();

            char[] ca = new char[] { 'a', 'a' };
            char[] cb = new char[] { 'b', 'b' };
            char[] cc = new char[] { 'c', 'c' };
            char[] cd = new char[] { 'd', 'd' };

            hashTest2.Add(new Tuple<char[], char[]>(ca, cb));
            hashTest2.Add(new Tuple<char[], char[]>(cc, cd));

            hashTest2.Clear();

            hashTest2.Add(new Tuple<char[], char[]>(ca, cb));

            ca[0] = 'c';
            ca[1] = 'c';
            cb[0] = 'd';
            cb[1] = 'd';

            hashTest2.Add(new Tuple<char[], char[]>(ca, cb));

            hashTest2.Clear();

            hashTest2.Add(new Tuple<char[], char[]>(ca, cb));

            ca[0] = 'a';
            ca[1] = 'a';
            cb[0] = 'b';
            cb[1] = 'b';
            cc[0] = 'a';
            cc[1] = 'a';
            cd[0] = 'b';
            cd[1] = 'b';

            var test2 = new Tuple<char[], char[]>(cc, cd);

            bool flag2 = hashTest2.Contains(test2);
            hashTest2.Add(test2);
#endif

            var hash = new Dictionary<Tuple<string, string>, long>();

            int redLetterIndex = 0;
            int blueLetterIndex = 0;

            int loopCount = (int)Math.Pow(2, n);
            for (uint bit = 0; bit < loopCount; ++bit)
            {
                uint bit_ = bit;

                for (int i = 0; i < n; ++i)
                {
                    bool flag = (bit_ & 0x1) == 0x1;
                    bit_ >>= 1;

                    if (flag)
                    {
                        red[redLetterIndex] = upperHalf[i];
                        ++redLetterIndex;
                    }
                    else
                    {
                        blue[blueLetterIndex] = upperHalf[i];
                        ++blueLetterIndex;
                    }
                }

                var tuple = new Tuple<string, string>(new string(red), new string(blue));

                if (hash.ContainsKey(tuple))
                {
                    ++hash[tuple];
                }
                else
                {
                    hash.Add(tuple, 1);
                }

                Array.Clear(red, '\0', n);
                redLetterIndex = 0;
                Array.Clear(blue, '\0', n);
                blueLetterIndex = 0;
            }

            long foundCount = 0;

            s.CopyTo(n, lowerHalf, 0, n);
            Array.Reverse(lowerHalf);

            for (uint bit = 0; bit < loopCount; ++bit)
            {
                uint bit_ = bit;

                for (int j = 0; j < n; ++j)
                {
                    bool flag = (bit_ & 0x1) == 0x1;
                    bit_ >>= 1;

                    if (flag)
                    {
                        blue[blueLetterIndex] = lowerHalf[j];
                        ++blueLetterIndex;
                    }
                    else
                    {
                        red[redLetterIndex] = lowerHalf[j];
                        ++redLetterIndex;
                    }
                }

                var tuple = new Tuple<string, string>(new string(red), new string(blue));

                if (hash.ContainsKey(tuple))
                {
                    foundCount += hash[tuple];
                }

                Array.Clear(red, '\0', n);
                redLetterIndex = 0;
                Array.Clear(blue, '\0', n);
                blueLetterIndex = 0;
            }


            Console.WriteLine("{0}", foundCount);
        }

#if false
        static void Main(string[] args)
        {
            int n = int.Parse(Console.ReadLine());
            string s = Console.ReadLine();

            long foundCount = 0;

            CheckFromLeft(n, ref foundCount, 0, 2 * n - 1, 0, s.ToCharArray());

            Console.WriteLine("{0}", foundCount);

            long a = nCr(5, 3);
            //long b = nCr(36, 18);

            Tuple<string, string> t = new Tuple<string, string>("abc", "d");
            Tuple<string, string> t2 = new Tuple<string, string>("abc", "d");

            var hash = new HashSet<Tuple<string, string>>();

            hash.Add(t);

            bool flag = hash.Contains(t2);
        }


        static long nCr(int n, int r)
        {
            return kaijou2(n, r + 1) / kaijou(n - r);
        }

        static long kaijou(int num)
        {
            long ans = 1;

            for (int i = num; i > 0; --i)
            {
                ans *= i;
            }

            return ans;
        }

        static long kaijou2(int numStart, int numEnd)
        {
            checked
            {
                long ans = 1;

                for (int i = numStart; i >= numEnd; --i)
                {
                    ans *= i;
                }

                return ans;
            }
        }

        static void CheckFromLeft(int n, ref long foundCount, int currentLeftPosition, int currentRightPosition, int letterCount, char[] stringOriginal)
        {
            if (currentLeftPosition + (n - letterCount) > 2 * n)
            {
                return;
            }

            int i = currentLeftPosition;
            int end = n + letterCount + 1;
            for (; i < end; ++i)
            {
                if (stringOriginal[i] != ' ') break;
            }
            if (i == end)
            {
                return;
            }

            char[] string_ = new char[2 * n];
            Array.Copy(stringOriginal, string_, 2 * n);

            char letter = string_[i];
            string_[i] = ' ';
            CheckFromRight(letter, n, ref foundCount, i + 1, currentRightPosition, letterCount, string_);

            CheckFromLeft(n, ref foundCount, i + 1, currentRightPosition, letterCount, stringOriginal);
        }

        static void CheckFromRight(char letter, int n, ref long foundCount, int currentLeftPosition, int currentRightPosition, int letterCount, char[] stringOriginal)
        {
            if (currentRightPosition - (n - letterCount) < -1)
            {
                return;
            }

            int i = currentRightPosition;
            for (; i >= 0; --i)
            {
                if (stringOriginal[i] == letter) break;
            }
            if (i == -1)
            {
                return;
            }

            char[] string_ = new char[2 * n];
            Array.Copy(stringOriginal, string_, 2 * n);

            if (letterCount+1 == n)
            {
                ++foundCount;
                return;
            }

            string_[i] = ' ';
            CheckFromLeft(n, ref foundCount, currentLeftPosition, i - 1, letterCount+1, string_);

            CheckFromRight(letter, n, ref foundCount, currentLeftPosition, i - 1, letterCount, stringOriginal);
        }
#endif
    }
}