Problem:
J: Tiles are Colorful

ICPC で良い成績を収めるには修行が欠かせない．うさぎは ICPC で勝ちたいので，今日も修行をすることにした．

今日の修行は，流行りのパズルをすばやく解いて，瞬発力を鍛えようというものである．今日挑戦するのは，色とりどりのタイルが並んでいてそれらを上手く消していくパズルだ

初期状態では，グリッド上のいくつかのマスにタイルが置かれている．各タイルには色がついている．プレイヤーはゲーム開始後，以下の手順で示される操作を何回も行うことができる．

タイルが置かれていないマスを 1 つ選択し，そのマスを叩く．

叩いたマスから上に順に辿っていき，タイルが置かれているマスに至ったところでそのタイルに着目する．タイルが置かれているマスがないまま盤面の端に辿り着いたら何にも着目しない．

同様の操作を叩いたマスから下・左・右方向に対して行う．最大 4 枚のタイルが着目されることになる．

着目したタイルの中で同じ色のものがあれば，それらのタイルを盤面から取り除く．同じ色のタイルの組が 2 組あれば，それら両方を取り除く．

タイルを取り除いた枚数と同じ値の得点が入る．

着目をやめる．

たとえば，以下のような状況を考えよう．タイルが置かれていないマスはピリオドで，タイルの色はアルファベット大文字で表されている．

..A.......
.......B..
..........
..B.......
..A.CC....

ここで上から 2 行目，左から 3 列目のマスを叩く操作を考える．着目することになるタイルは A , B , B の 3 枚であるから，B の 2 枚が消えて盤面は以下のようになり，2 点を得る．

..A.......
..........
..........
..........
..A.CC....

このパズルはゆっくりしていると時間切れになってしまい，盤面の一部が見えなくなりどのくらい修行が足りなかったのかがわからなくなってしまう．
各色のタイルは 2 枚ずつ置かれているが，それらをすべて消せるとは限らないので，予めプログラムに得点の最大値を計算させておきたい．

Input

M N
C1,1C1,2...C1,N
C2,1C2,2...C2,N
...
CM,1CM,2...CM,N

整数 M, N は盤が 縦 M × 横 N のマス目であることを表す．Ci, j はアルファベット大文字またはピリオド ( . ) であり，上から i 行目，左から j 列目のマスについて，アルファベット大文字の場合は置かれているタイルの色を表し，ピリオドの場合はこのマスにタイルが置かれていないことを表す．

1 ≤ M ≤ 500，1 ≤ N ≤ 500 を満たす．各アルファベット大文字は入力中に 0 個または 2 個現れる．

Output

得点の最大値を 1 行に出力せよ．

Sample Input 1

5 10
..A.......
.......B..
..........
..B.......
..A.CC....

Sample Output 1

4

Sample Input 2

3 3
ABC
D.D
CBA

Sample Output 2

4

Sample Input 3

5 7
NUTUBOR
QT.SZRQ
SANAGIP
LMDGZBM
KLKIODP

Sample Output 3

34

Solution in C# (judge verdict: Accepted)

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

public class P
{
    public int x { get; set; }
    public int y { get; set; }
}

public class hello
{
    public static int h, w;
    public static void Main()
    {
        string[] line = Console.ReadLine().Trim().Split(' ');
        h = int.Parse(line[0]);
        w = int.Parse(line[1]);
        var map = new int[h, w];
        var ps = new List<P>[26];
        for (int i = 0; i < 26; i++) ps[i] = new List<P>();
        for (int i = 0; i < h; i++)
        {
            var s = Console.ReadLine().Trim();
            for (int j = 0; j < w; j++)
            {
                if (s[j] == '.') map[i, j] = -1;
                else
                {
                    var t = s[j] - 'A';
                    map[i, j] = t;
                    ps[t].Add(new P { x = i, y = j });
                }
            }
        }
        var d0 = new List<int>[26];
        var d1 = new List<int>[26];
        for (int i = 0; i < 26; i++)
        {
            d0[i] = new List<int>();
            d1[i] = new List<int>();
        }
        var root1 = new bool[26];
        makeList(map, ps, d0, d1, root1);
        getAns(ps, d0, d1, root1);
    }
    static void getAns(List<P>[] ps, List<int>[] d0, List<int>[] d1, bool[] root1)
    {
        var res = 0;
        var fin = new bool[26];
        while (true)
        {
            var rf = false;
            for (int i = 0; i < 26; i++)
            {
                if (ps[i].Count() == 2 && !fin[i])
                {
                    if ((root1[i] && d0[i].Count() == 0) |
                            (!root1[i]) && (d0[i].Count() == 0 | d1[i].Count() == 0))
                    {
                        res += 2;
                        rf = true;
                        fin[i] = true;
                        for (int j = 0; j < 26; j++)
                        {
                            d0[j].Remove(i);
                            if (!root1[j]) d1[j].Remove(i);
                        }
                    }
                }
            }
            if (!rf) break;
        }
        Console.WriteLine(res);
    }
    static void makeList(int[,] map, List<P>[] ps, List<int>[] d0, List<int>[] d1, bool[] root1)
    {
        for (int i = 0; i < 26; i++)
        {
            if (ps[i].Count() == 2)
            {
                var x0 = ps[i][0].x;
                var y0 = ps[i][0].y;
                var x1 = ps[i][1].x;
                var y1 = ps[i][1].y;

                if (x0 == x1)
                {
                    root1[i] = true;
                    if (y1 - y0 == 1) d0[i].Add(30);
                    else
                    {
                        for (int j = y0 + 1; j < y1; j++)
                        {
                            var te = map[x0, j];
                            if (te >= 0) d0[i].Add(te);
                        }
                    }
                }
                else if (y0 == y1)
                {
                    root1[i] = true;
                    if (x1 - x0 == 1) d0[i].Add(30);
                    else
                    {
                        for (int j = x0 + 1; j < x1; j++)
                        {
                            var te = map[j, y0];
                            if (te >= 0) d0[i].Add(te);
                        }
                    }
                }
                else
                {
                    if (y1 > y0)
                    {
                        for (int j = y0 + 1; j <= y1; j++)
                        {
                            var te = map[x0, j];
                            if (te >= 0) d0[i].Add(te);
                        }
                        for (int j = x0 + 1; j < x1; j++)
                        {
                            var te = map[j, y1];
                            if (te >= 0) d0[i].Add(te);
                        }
                        for (int j = x0 + 1; j <= x1; j++)
                        {
                            var te = map[j, y0];
                            if (te >= 0) d1[i].Add(te);
                        }
                        for (int j = y0 + 1; j < y1; j++)
                        {
                            var te = map[x1, j];
                            if (te >= 0) d1[i].Add(te);
                        }
                    }
                    else
                    {
                        for (int j = y1; j < y0; j++)
                        {
                            var te = map[x0, j];
                            if (te >= 0) d0[i].Add(te);
                        }
                        for (int j = x0 + 1; j < x1; j++)
                        {
                            var te = map[j, y1];
                            if (te >= 0) d0[i].Add(te);
                        }
                        for (int j = y1 + 1; j <= y0; j++)
                        {
                            var te = map[x1, j];
                            if (te >= 0) d1[i].Add(te);
                        }
                        for (int j = x0 + 1; j < x1; j++)
                        {
                            var te = map[j, y0];
                            if (te >= 0) d1[i].Add(te);
                        }
                    }
                }
            }
        }
        for (int i = 0; i < 26; i++)
        {
            d0[i] = d0[i].Distinct().ToList();
            if (!root1[i]) d1[i] = d1[i].Distinct().ToList();
        }
    }
}