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)

// ReSharper disable ArrangeTypeMemberModifiers
// ReSharper disable ConvertIfStatementToSwitchStatement
// ReSharper disable FunctionRecursiveOnAllPaths
// ReSharper disable InconsistentNaming
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable InvertIf
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable MemberCanBeMadeStatic.Global
// ReSharper disable MemberCanBeMadeStatic.Local
// ReSharper disable NonReadonlyMemberInGetHashCode
// ReSharper disable PossibleNullReferenceException
// ReSharper disable RedundantUsingDirective
// ReSharper disable SuggestVarOrType_BuiltInTypes
// ReSharper disable SuggestVarOrType_Elsewhere
// ReSharper disable TailRecursiveCall
// ReSharper disable UnusedMember.Global
// ReSharper disable UnusedMember.Local
// ReSharper disable UseObjectOrCollectionInitializert

#if true && UTAKA_LOCAL

//#if false
#define UTAKA_DEBUG
#endif

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;

namespace UtakaApp
{
    public partial class Program
    {
        public const string SiteName    = "AtCoder";
        public const string ContestName = "agc026";
        public const string ProblemName = "C";

        public void Solve()
        {
            int    N = cin.ReadInt;
            string S = cin.Read;

            string s1 = S.Substring(0, N);
            string s2 = S.Substring(N, N);

            s2 = new string(s2.ToCharArray().Reverse().ToArray());

            List<char> c1r = new List<char>();
            List<char> c1b = new List<char>();

            List<char> c2r = new List<char>();
            List<char> c2b = new List<char>();

            Dictionary<Pair<string, string>, long> s1Dict = new Dictionary<Pair<string, string>, long>();
            Dictionary<Pair<string, string>, long> s2Dict = new Dictionary<Pair<string, string>, long>();

            for (int bit = 0; bit < (1 << N); bit++)
            {
                c1r.Clear();
                c1b.Clear();
                c2r.Clear();
                c2b.Clear();

                for (int i = 0; i < N; i++)
                {
                    if ((bit & (1 << i)) != 0)
                    {
                        c1r.Add(s1[i]);
                        c2r.Add(s2[i]);
                    }
                    else
                    {
                        c1b.Add(s1[i]);
                        c2b.Add(s2[i]);
                    }
                }

                string res1r = new string(c1r.ToArray());
                string res1b = new string(c1b.ToArray());
                string res2r = new string(c2r.ToArray());
                string res2b = new string(c2b.ToArray());

                Pair p1 = new Pair(res1r, res1b);
                Pair p2 = new Pair(res2r, res2b);

                if (!s1Dict.ContainsKey(p1))
                {
                    s1Dict.Add(p1, 0);
                }

                s1Dict[p1]++;

                if (!s2Dict.ContainsKey(p2))
                {
                    s2Dict.Add(p2, 0);
                }

                s2Dict[p2]++;
            }

            long ans = 0;

            foreach (Pair<string, string> pair in s1Dict.Keys)
            {
                if (!s2Dict.ContainsKey(pair))
                {
                    continue;
                }

                long count1 = s1Dict[pair];
                long count2 = s2Dict[pair];

                ans += count1 * count2;
            }

            cou.WriteLine(ans);
        }
    }

    public sealed class Pair : Pair<string, string>
    {
        public Pair(string first, string second) : base(first, second)
        {
        }
    }

    [DebuggerDisplay("({First}, {Second})")]
    public class Pair<T1, T2> : IComparable<Pair<T1, T2>>, IEquatable<Pair<T1, T2>>
        where T1 : IComparable<T1>
        where T2 : IComparable<T2>
    {
        public Pair(T1 first, T2 second)
        {
            First  = first;
            Second = second;
        }

        public T1 First;
        public T2 Second;

        public int CompareTo(Pair<T1, T2> other)
        {
            if (ReferenceEquals(this, other)) return 0;
            if (ReferenceEquals(null, other)) return 1;
            var firstComparison = First.CompareTo(other.First);
            return firstComparison != 0 ? firstComparison : Second.CompareTo(other.Second);
        }

        public override string ToString() => $"({First}, {Second})";

        public bool Equals(Pair<T1, T2> other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return EqualityComparer<T1>.Default.Equals(First, other.First) &&
                   EqualityComparer<T2>.Default.Equals(Second, other.Second);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            return obj.GetType() == this.GetType() && Equals((Pair<T1, T2>)obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return (EqualityComparer<T1>.Default.GetHashCode(First) * 397) ^
                       EqualityComparer<T2>.Default.GetHashCode(Second);
            }
        }
    }


//=========================================================================================
// Debug IO
//=========================================================================================

    public partial class Program
    {
        //=========================================================================================
        // Main
        //=========================================================================================

        private readonly ConsoleInput cin;
        private readonly IConsole     cou;

        public Program(ConsoleInput cin, IConsole cou)
        {
            this.cin = cin;
            this.cou = cou;
        }

        public static void Main(string[] args)
        {
#if UTAKA_LOCAL
//#if false
            new TestCaseCheckerForAtCoder().TestProblems();
#else
            var cin = new ConsoleInput(System.Console.In);
            var console = new MyConsole();
            new Program(cin, console).Solve();
            console.Flush();
#endif
        }

        //=========================================================================================
        // Library
        //=========================================================================================

        public static T[] Array1D<T>(int a, T defaultValue)
            where T : struct
        {
            var ret = new T[a];
            for (int i = 0; i < a; i++)
            {
            }

            return ret;
        }

        public static T[] Array1D<T>(int a)
            where T : struct
        {
            var ret = new T[a];
            return ret;
        }

        /// <summary>
        /// 要素数 (a, b) の、defaultValue で満たされた配列を作成します。
        /// </summary>
        /// <typeparam name="T">配列の型</typeparam>
        /// <param name="a">1次元の要素数</param>
        /// <param name="b">2次元の要素数</param>
        /// <param name="defaultValue">デフォルト値</param>
        /// <returns>指定した条件で初期化された配列</returns>
        public static T[,] Array2D<T>(int a, int b, T defaultValue)
            where T : struct
        {
            var ret = new T[a, b];
            for (int i = 0; i < a; i++)
            {
                for (int j = 0; j < b; j++)
                {
                    ret[i, j] = defaultValue;
                }
            }

            return ret;
        }

        public static T[,,] Array3D<T>(int a, int b, int c, T defaultValue)
            where T : struct
        {
            T[,,] ret = new T[a, b, c];

            for (int i = 0; i < a; i++)
            {
                for (int j = 0; j < b; j++)
                {
                    for (int k = 0; k < c; k++)
                    {
                        ret[i, j, k] = defaultValue;
                    }
                }
            }

            return ret;
        }

        /// <summary>
        /// 要素数 (a, b) の、defaultValue で満たされたJag配列を作成します。
        /// </summary>
        /// <typeparam name="T">配列の型</typeparam>
        /// <param name="a">1次元の要素数</param>
        /// <param name="b">2次元の要素数</param>
        /// <param name="defaultValue">デフォルト値</param>
        /// <returns>指定した条件で初期化された配列</returns>
        public static T[][] JagArray2D<T>(int a, int b, T defaultValue)
            where T : struct
        {
            var ret = new T[a][];
            for (int i = 0; i < a; i++)
            {
                var row = new T[b];
                ret[i] = row;

                for (int j = 0; j < b; j++)
                {
                    row[j] = defaultValue;
                }
            }

            return ret;
        }

        /// <summary>
        /// 要素数 (a, b) のdefault値で満たされたJag配列を作成します。
        /// </summary>
        /// <typeparam name="T">配列の型</typeparam>
        /// <param name="a">1次元の要素数</param>
        /// <param name="b">2次元の要素数</param>
        /// <returns>初期化された配列</returns>
        public static T[][] JagArray2D<T>(int a, int b)
            where T : struct
        {
            var ret = new T[a][];
            for (int i = 0; i < a; i++)
            {
                ret[i] = new T[b];
            }

            return ret;
        }

        public static T[][][] JagArray3D<T>(int a, int b, int c, T defaultValue)
            where T : struct
        {
            var ret = new T[a][][];
            for (int i = 0; i < a; i++)
            {
                T[][] aRow = new T[b][];
                ret[i] = aRow;

                for (int j = 0; j < b; j++)
                {
                    T[] bRow = new T[c];
                    aRow[j] = bRow;

                    for (int k = 0; k < c; k++)
                    {
                        bRow[k] = defaultValue;
                    }
                }
            }

            return ret;
        }

        public static T[][][] JagArray3D<T>(int a, int b, int c)
            where T : struct
        {
            var ret = new T[a][][];
            for (int i = 0; i < a; i++)
            {
                T[][] aRow = new T[b][];
                ret[i] = aRow;

                for (int j = 0; j < b; j++)
                {
                    T[] bRow = new T[c];
                    aRow[j] = bRow;
                }
            }

            return ret;
        }

        public static T[][][][] JagArray4D<T>(int a, int b, int c, int d, T defaultValue)
            where T : struct
        {
            var ret = new T[a][][][];
            for (int i = 0; i < a; i++)
            {
                T[][][] aRow = new T[b][][];
                ret[i] = aRow;

                for (int j = 0; j < b; j++)
                {
                    T[][] bRow = new T[c][];
                    aRow[j] = bRow;

                    for (int k = 0; k < c; k++)
                    {
                        T[] cRow = new T[d];
                        bRow[k] = cRow;

                        for (int l = 0; l < d; l++)
                        {
                            cRow[l] = defaultValue;
                        }
                    }
                }
            }

            return ret;
        }

        public static T[][][][] JagArray4D<T>(int a, int b, int c, int d)
            where T : struct
        {
            var ret = new T[a][][][];
            for (int i = 0; i < a; i++)
            {
                T[][][] aRow = new T[b][][];
                ret[i] = aRow;

                for (int j = 0; j < b; j++)
                {
                    T[][] bRow = new T[c][];
                    aRow[j] = bRow;

                    for (int k = 0; k < c; k++)
                    {
                        T[] cRow = new T[d];
                        bRow[k] = cRow;
                    }
                }
            }

            return ret;
        }

        public static T[][][][][] JagArray5D<T>(int a, int b, int c, int d, int e, T defaultValue)
            where T : struct
        {
            var ret = new T[a][][][][];
            for (int i = 0; i < a; i++)
            {
                T[][][][] aRow = new T[b][][][];
                ret[i] = aRow;

                for (int j = 0; j < b; j++)
                {
                    T[][][] bRow = new T[c][][];
                    aRow[j] = bRow;

                    for (int k = 0; k < c; k++)
                    {
                        T[][] cRow = new T[d][];
                        bRow[k] = cRow;

                        for (int l = 0; l < d; l++)
                        {
                            T[] dRow = new T[e];
                            cRow[l] = dRow;

                            for (int m = 0; m < e; m++)
                            {
                                dRow[m] = defaultValue;
                            }
                        }
                    }
                }
            }

            return ret;
        }

        public static T[][][][][] JagArray5D<T>(int a, int b, int c, int d, int e)
            where T : struct
        {
            var ret = new T[a][][][][];
            for (int i = 0; i < a; i++)
            {
                T[][][][] aRow = new T[b][][][];
                ret[i] = aRow;

                for (int j = 0; j < b; j++)
                {
                    T[][][] bRow = new T[c][][];
                    aRow[j] = bRow;

                    for (int k = 0; k < c; k++)
                    {
                        T[][] cRow = new T[d][];
                        bRow[k] = cRow;

                        for (int l = 0; l < d; l++)
                        {
                            T[] dRow = new T[e];
                            cRow[l] = dRow;
                        }
                    }
                }
            }

            return ret;
        }

        /// <summary>
        /// ジャグ配列をコピーして返す。
        /// </summary>
        public static T[][] CopyJagArray2D<T>(T[][] source)
            where T : struct
        {
            int   len  = source.Length;
            T[][] dest = new T[len][];

            for (int i = 0; i < len; i++)
            {
                T[] inner       = source[i];
                int innerLength = inner.Length;
                T[] newer       = new T[innerLength];
                Array.Copy(inner, newer, innerLength);
                dest[i] = newer;
            }

            return dest;
        }
    }

    public class IntLargeComparer : IComparer<int>
    {
        public static IntLargeComparer Instance = new IntLargeComparer();

        public int Compare(int x, int y)
        {
            return y - x;
        }
    }

    public class LongLargeComparer : IComparer<long>
    {
        public static LongLargeComparer Instance = new LongLargeComparer();

        public int Compare(long x, long y)
        {
            if (x > y) return -1;
            if (x < y) return 1;
            return 0;
        }
    }

    public static class UtakaExtentions
    {
        /// <summary>
        /// 配列をコピーします
        /// </summary>
        public static T[] Copy<T>(this IList<T> a)
        {
            T[] ret = new T[a.Count];

            for (int i = 0; i < a.Count; i++) ret[i] = a[i];

            return ret;
        }
    }

    public class ConsoleInput
    {
        private readonly char          _separator = ' ';
        private readonly TextReader    _stream;
        private readonly Queue<string> inputStream;

        public ConsoleInput(TextReader stream, char separator = ' ')
        {
            _separator  = separator;
            _stream     = stream;
            inputStream = new Queue<string>();
        }

        public string Read
        {
            get
            {
                if (inputStream.Count != 0)
                {
                    return inputStream.Dequeue();
                }

                var tmp = _stream.ReadLine().Split(_separator);
                for (var i = 0; i < tmp.Length; ++i)
                {
                    inputStream.Enqueue(tmp[i]);
                }

                return inputStream.Dequeue();
            }
        }

        public string ReadLine   => _stream.ReadLine();
        public int    ReadInt    => int.Parse(Read);
        public long   ReadLong   => long.Parse(Read);
        public double ReadDouble => double.Parse(Read);

        public string[] ReadStrArray(long N)
        {
            var ret = new string[N];
            for (long i = 0; i < N; ++i)
            {
                ret[i] = Read;
            }

            return ret;
        }

        public int[] ReadIntArray(long N)
        {
            var ret = new int[N];
            for (long i = 0; i < N; ++i)
            {
                ret[i] = ReadInt;
            }

            return ret;
        }

        public long[] ReadLongArray(long N)
        {
            var ret = new long[N];
            for (long i = 0; i < N; ++i)
            {
                ret[i] = ReadLong;
            }

            return ret;
        }
    }

    public interface IConsole
    {
        void Flush();
        void Write(object obj);
        void Write(string str);
        void WriteLine(object obj);
        void WriteLine(string str);
        void WriteLine();
    }

    public class MyConsole : IConsole
    {
        public MyConsole()
        {
            var sw = new StreamWriter(Console.OpenStandardOutput()) {AutoFlush = false};
            Console.SetOut(sw);
        }

        public void Flush()
        {
            Console.Out.Flush();
        }

        public void Write(object obj)
        {
            Write(obj.ToString());
        }

        public void Write(string str)
        {
            Console.Write(str);
        }

        public void WriteLine(object obj)
        {
            WriteLine(obj.ToString());
        }

        public void WriteLine(string str)
        {
            Console.WriteLine(str);
        }

        public void WriteLine()
        {
            Console.WriteLine();
        }
    }

#if UTAKA_LOCAL

    public class DebugConsole : IConsole
    {
        private readonly StringBuilder mSb;

        public DebugConsole()
        {
            mSb = new StringBuilder();
        }

        public void Flush()
        {
            // 何もしない
        }

        public void Write(object obj)
        {
            Write(obj.ToString());
        }

        public void Write(string str)
        {
            mSb.Append(str);
        }

        public void WriteLine(object obj)
        {
            WriteLine(obj.ToString());
        }

        public void WriteLine(string str)
        {
            mSb.AppendLine(str);
        }

        public void WriteLine()
        {
            mSb.AppendLine();
        }

        public string GetAllOutput()
        {
            return mSb.ToString();
        }
    }

#endif

#if UTAKA_LOCAL

    public class TestCaseCheckerForAtCoder
    {
        private string GetDirectoryPath()
        {
            var problemPart = string.IsNullOrEmpty(Program.ProblemName)
                ? ""
                : $"/{Program.ProblemName}";

            return
                $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/AlgorithmPrac/{Program.SiteName}/{Program.ContestName}{problemPart}";
        }

        private string GetInputFilePath(int testCaseNumber)
        {
            return $"{GetDirectoryPath()}/sample-{testCaseNumber}.in";
        }

        private string GetOutputFilePath(int testCaseNumber)
        {
            return $"{GetDirectoryPath()}/sample-{testCaseNumber}.out";
        }

        public bool TestProblem(int testCaseNumber)
        {
            var inputFilePath  = GetInputFilePath(testCaseNumber);
            var outputFilePath = GetOutputFilePath(testCaseNumber);

            TextReader inputStream = new StreamReader(inputFilePath);
            var        cin         = new ConsoleInput(inputStream);

            var debugConsoleWriter = new DebugConsole();

            new Program(cin, debugConsoleWriter).Solve();

            var output = debugConsoleWriter.GetAllOutput();

            TextReader outputStream = new StreamReader(outputFilePath);
            var        outputAnswer = outputStream.ReadToEnd();

            Dbg.WriteLine(output);
            Dbg.WriteLine(outputAnswer);

            var isCorrect = output == outputAnswer;

            return isCorrect;
        }

        public void TestProblems(int targetTestCaseNumber = -1)
        {
            if (targetTestCaseNumber >= 0)
            {
                Console.WriteLine($"!!!!!!!!!!!! Check TestCase {targetTestCaseNumber} !!!!!!!!!!");
            }

            bool isSuccessAll = true;

            int testCaseNumber = 0;
            while (true)
            {
                testCaseNumber++;

                bool foundTargetTest = targetTestCaseNumber == testCaseNumber;

                if (targetTestCaseNumber >= 0 && !foundTargetTest)
                {
                    continue;
                }

                var inputFileName = GetInputFilePath(testCaseNumber);
                if (!File.Exists(inputFileName))
                {
                    break;
                }

                Console.WriteLine($"TestCase {testCaseNumber} =====================================================");

                var result = TestProblem(testCaseNumber);

                if (result)
                {
                    Console.WriteLine("Success");
                }
                else
                {
                    isSuccessAll = false;
                    Console.WriteLine("Failure *****");
                }

                if (foundTargetTest)
                {
                    break;
                }
            }

            if (isSuccessAll)
            {
                Console.WriteLine("!!!!!!!!! All Success !!!!!!!!!");
            }
        }
    }

#endif

    public static class Dbg
    {
        [Conditional("UTAKA_DEBUG")]
        public static void WriteLine(string str)
        {
            Console.WriteLine(str);
        }

        [Conditional("UTAKA_DEBUG")]
        public static void WriteLine()
        {
            Console.WriteLine();
        }

        [Conditional("UTAKA_DEBUG")]
        public static void Write(string str)
        {
            Console.Write(str);
        }
    }
}