Problem:
Score : 400 points

Problem Statement

The ABC number of a string T is the number of triples of integers (i, j, k) that satisfy all of the following conditions:

1 ≤ i < j < k ≤ |T| (|T| is the length of T.)

T_i = A (T_i is the i-th character of T from the beginning.)

T_j = B

T_k = C

For example, when T = ABCBC, there are three triples of integers (i, j, k) that satisfy the conditions: (1, 2, 3), (1, 2, 5), (1, 4, 5). Thus, the ABC number of T is 3.

You are given a string S. Each character of S is A, B, C or ?.

Let Q be the number of occurrences of ? in S. We can make 3^Q strings by replacing each occurrence of ? in S with A, B or C. Find the sum of the ABC numbers of all these strings.

This sum can be extremely large, so print the sum modulo 10^9 + 7.

Constraints

3 ≤ |S| ≤ 10^5

Each character of S is A, B, C or ?.

Input

Input is given from Standard Input in the following format:

S

Output

Print the sum of the ABC numbers of all the 3^Q strings, modulo 10^9 + 7.

Sample Input 1

A??C

Sample Output 1

8

In this case, Q = 2, and we can make 3^Q = 9 strings by by replacing each occurrence of ? with A, B or C. The ABC number of each of these strings is as follows:

AAAC: 0

AABC: 2

AACC: 0

ABAC: 1

ABBC: 2

ABCC: 2

ACAC: 0

ACBC: 1

ACCC: 0

The sum of these is 0 + 2 + 0 + 1 + 2 + 2 + 0 + 1 + 0 = 8, so we print 8 modulo 10^9 + 7, that is, 8.

Sample Input 2

ABCBC

Sample Output 2

3

When Q = 0, we print the ABC number of S itself, modulo 10^9 + 7. This string is the same as the one given as an example in the problem statement, and its ABC number is 3.

Sample Input 3

????C?????B??????A???????

Sample Output 3

979596887

In this case, the sum of the ABC numbers of all the 3^Q strings is 2291979612924, and we should print this number modulo 10^9 + 7, that is, 979596887.

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

using System;
using System.Linq;
using System.Collections.Generic;
using Library;
using static Library.Comfort;
using static Library.Math;
using static Library.Algorithm;
using static System.Math;
using static System.Console;
using System.Collections;

namespace AtCoderer
{
    public class App
    {
        public static void Solve()
        {
            var str = Console.ReadLine();
            long[,] dp = new long[str.Length + 1, 4];
            dp[0, 0] = 1;
            for (var i = 1; i <= str.Length; i++)
            {
                if ('A' == str[i - 1])
                    dp[i, 1] += dp[i - 1, 0];
                if ('B' == str[i - 1])
                    dp[i, 2] += dp[i - 1, 1];
                if ('C' == str[i - 1])
                    dp[i, 3] += dp[i - 1, 2];
                if (str[i - 1] == '?')
                {
                    dp[i, 0] += 3 * dp[i - 1, 0];
                    dp[i, 1] += 3 * dp[i - 1, 1];
                    dp[i, 2] += 3 * dp[i - 1, 2];
                    dp[i, 3] += 3 * dp[i - 1, 3];
                    dp[i, 1] += dp[i - 1, 0];
                    dp[i, 2] += dp[i - 1, 1];
                    dp[i, 3] += dp[i - 1, 2];
                }
                else
                {
                    dp[i, 0] += dp[i - 1, 0];
                    dp[i, 1] += dp[i - 1, 1];
                    dp[i, 2] += dp[i - 1, 2];
                    dp[i, 3] += dp[i - 1, 3];
                }

                dp[i, 0] %= mod;
                dp[i, 1] %= mod;
                dp[i, 2] %= mod;
                dp[i, 3] %= mod;
            }
            Console.WriteLine(dp[str.Length, 3]);
        }
        public static void Main(string[] args)
        {
            Solve();
            Console.ReadKey();
        }
    }
}
namespace Library
{ 
    public static class Comfort
    {
        public const int mod = 1000000007;
        public class Point
        {
            public int a;
            public int b;
            public Point(int a, int b)
            {
                this.a = a;
                this.b = b;
            }
        }
        public static void DWrite<T>(T o)
        {
#if DEBUG
            Console.WriteLine(o);    
#endif
        }
        public static int[] OrderBy(this int[] IArr)
        {
            int[] arr = new int[IArr.Length];
            Array.Copy(IArr, arr, IArr.Length);
            Array.Sort(arr);
            return arr;
        }
        public static char[] OrderBy(this string IArr)
        {
            char[] arr = new char[IArr.Length];
            Array.Copy(IArr.ToCharArray(), arr, IArr.Length);
            Array.Sort(arr);
            return arr;
        }
        public static int Max(params int[] p) => p.Max();
        public static long Max(params long[] p) => p.Max();
        public static int Min(params int[] p) => p.Min();
        public static long Min(params long[] p) => p.Min();
        public static int Parse()
        {
            return int.Parse(Console.ReadLine());
        }
        public static int[] ParseArray()
        {
            return Console.ReadLine().Split().Select(v => int.Parse(v)).ToArray();
        }
        public static long[] ParseArrayL()
        {
            return Console.ReadLine().Split().Select(v => long.Parse(v)).ToArray();
        }
        public static void ParseArray(out int a, out int b)
        {
            var r = Console.ReadLine().Split().Select(v => int.Parse(v)).ToArray();
            a = r[0];
            b = r[1];
        }
        public static void ParseArray(out int a, out int b, out int c)
        {
            var r = Console.ReadLine().Split().Select(v => int.Parse(v)).ToArray();
            a = r[0];
            b = r[1];
            c = r[2];
        }
        public static void ParseArray(out int a, out int b, out int c, out int d)
        {
            var r = Console.ReadLine().Split().Select(v => int.Parse(v)).ToArray();
            a = r[0];
            b = r[1];
            c = r[2];
            d = r[3];
        }
        public static void ParseArray(out long a, out long b)
        {
            var r = Console.ReadLine().Split().Select(v => long.Parse(v)).ToArray();
            a = r[0];
            b = r[1];
        }
        public static void ParseArray(out long a, out long b, out long c)
        {
            var r = Console.ReadLine().Split().Select(v => long.Parse(v)).ToArray();
            a = r[0];
            b = r[1];
            c = r[2];
        }
        public static void ParseArray(out long a, out long b, out long c, out long d)
        {
            var r = Console.ReadLine().Split().Select(v => long.Parse(v)).ToArray();
            a = r[0];
            b = r[1];
            c = r[2];
            d = r[3];
        }
        public static string ToString(this IEnumerable<char> v)
        {
            return string.Join("", v);
        }
        public static string Join<T>(this IEnumerable<T> v)
        {
            return string.Join(" ", v);
        }
        public static string Join<T>(this IEnumerable<T> v, char c)
        {
            return string.Join(c.ToString(), v);
        }
        public static string Join<T>(this IEnumerable<T> v, string s)
        {
            return string.Join(s, v);
        }
    }
    public static class Math
    {
        public static bool IsNatural(this float v) => v - (int)v == 0;
        public static bool IsNatural(this double v) => v - (int)v == 0;
        public static long nCm(long n, long m)
        {
            if (m == 0) return 1;
            if (n == 0) return 0;
            return n * nCm(n - 1, m - 1) / m % 1000000007;
        }
        public static IEnumerable<int> Factor(int n)
        {
            int i = 2;
            int tmp = n;

            while (i * i <= n)
            {
                if (tmp % i == 0)
                {
                    tmp /= i;
                    yield return i;
                }
                else
                {
                    i++;
                }
            }
            if (tmp != 1) yield return tmp;
        }
        public static int Gcd(int a, int b) => a > b ? GcdRecursive(a, b) : GcdRecursive(b, a);
        private static int GcdRecursive(int x, int y) => y == 0 ? x : GcdRecursive(y, x % y);
        public static long Gcd(long a, long b) => a > b ? GcdRecursive(a, b) : GcdRecursive(b, a);
        private static long GcdRecursive(long x, long y) => y == 0 ? x : GcdRecursive(y, x % y);
        public static long Lcm(int a, int b) => ((long)a) * b / Gcd(a, b);
    }
    public static class Algorithm
    {
        public class EnumAll
        {
            bool Over = false;
            int UpperValue;
            int Dimension;
            public int[] Current { private set; get; }
            public EnumAll(int MaxValue, int Dimension)
            {
                this.Dimension = Dimension;
                UpperValue = MaxValue;
                Current = new int[Dimension];
                Current[0] = -1;
            }
            public bool Next()
            {
                if (Over)
                    return false;
                bool flag = false;
                for (int i = 0; i < Dimension; i++)
                {
                    if (++Current[i] >= UpperValue)
                        Current[i] = 0;
                    else
                    {
                        flag = true;
                        break;
                    }
                }
                if (!flag)
                {
                    Over = true;
                    return false;
                }
                return true;
            }
        }

        public class Graph
        {
            public int Count = -1;
            private HashSet<int>[] Joined;
            public static Graph GenerateComplete(int size)
            {
                throw new NotImplementedException();
            }
            public Graph(int size)
            {
                Joined = new HashSet<int>[size];
                for (int i = 0; i < Joined.Length; i++)
                    Joined[i] = new HashSet<int>();
                Count = size;
            }
            public int[] GetConnected(int index)
            {
                return Joined[index].ToArray();
            }
            public void AddEdge(int a,int b)
            {
                if (a == b)
                    DWrite("ループを追加することはできません A:" + a + " B:" + b);
                else
                {
                    if (!Joined[a].Add(b))
                        DWrite("多重辺を追加することはできません A:" + a + " B:" + b);
                    else
                        Joined[b].Add(a);
                }
            }
            public int[] ToTree(int Root)
            {
                var ar = new int[Count];
                for (int i = 0; i < ar.Length; i++)
                    ar[i] = -10000000;
                ar[Root] = -1;
                Queue<int> bfs = new Queue<int>();
                bfs.Enqueue(Root);
                while(bfs.Count != 0)
                {
                    var t = bfs.Dequeue();
                    foreach(var v in Joined[t])
                    {
                        bfs.Enqueue(v);
                        if (ar[v] == -10000000)
                            ar[v] = t;
                    }
                }
                return ar;
            }
        }
        public class UnionFind
        {
            private int[] Parent;
            private int GetRoot(int v)
            {
                if (Parent[v] < 0)
                    return v;
                else
                    return Parent[v] = GetRoot(Parent[v]);
            }
            public UnionFind(int size)
            {
                Parent = new int[size];
                for (int i = 0; i < size; i++)
                    Parent[i] = -1;
            }
            public bool AreGrouped(int a, int b)
            {
                return GetRoot(a) == GetRoot(b);
            }
            public void Connect(int a, int b)
            {
                a = GetRoot(a);
                b = GetRoot(b);
                if (a == b) return;
                if (Parent[b] < Parent[a])
                {
                    Parent[b] += Parent[a];
                    Parent[a] = b;
                }
                else
                {
                    Parent[a] += Parent[b];
                    Parent[b] = a;
                }
            }
            public int GetGroupSize(int a)
            {
                return -Parent[GetRoot(a)];
            }
        }
    }
}