// #ifdef DEBUG
// #define _GLIBCXX_DEBUG
// #endif
#include <iostream>
#include <iomanip>
#include <vector>
#include <valarray>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <stack>
#include <bitset>
#include <utility>
#include <numeric>
#include <algorithm>
#include <functional>
#include <complex>
#include <string>
#include <sstream>

#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cstring>

// these require C++11
#include <unordered_set>
#include <unordered_map>
#include <random>
#include <thread>
#include <chrono>

using namespace std;

#define int long long

#define all(c) c.begin(), c.end()
#define repeat(i, n) for (int i = 0; i < static_cast<int>(n); i++)
#define debug(x) #x << "=" << (x)

#ifdef DEBUG
#define dump(x) std::cerr << debug(x) << " (L:" << __LINE__ << ")" << std::endl
#else
#define dump(x) 
#endif

template<typename A,typename B>
ostream &operator<<(ostream&os,const pair<A,B>& p){
    os << "(" << p.first << "," << p.second << ")";
    return os;
}

// Define a type which holds an unsigned integer value 
template<std::size_t> struct int_{};
template <class Tuple, size_t Pos>
std::ostream& print_tuple(std::ostream& out, const Tuple& t, int_<Pos> ) {
  out << std::get< std::tuple_size<Tuple>::value-Pos >(t) << ',';
  return print_tuple(out, t, int_<Pos-1>());
}
 
template <class Tuple>
std::ostream& print_tuple(std::ostream& out, const Tuple& t, int_<1> ) {
  return out << std::get<std::tuple_size<Tuple>::value-1>(t);
}
 
template <class... Args>
ostream& operator<<(ostream& out, const std::tuple<Args...>& t) {
  out << '('; 
  print_tuple(out, t, int_<sizeof...(Args)>()); 
  return out << ')';
}
template <typename T>
ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "[";
    for (const auto &v : vec) {
        os << v << ",";
    }
    os << "]";
    return os;
}



typedef complex<double> point;

// template<typename T,std::size_t N>
// struct _v_traits {using type = std::vector<typename _v_traits<T,N-1>::type>;};
// template<typename T>
// struct _v_traits<T,1> {using type = std::vector<T>;};
// template<typename T,std::size_t N=1>
// using vec = typename _v_traits<T,N>::type;


const char dot = '.';
inline int to_index(char c){
    return c - 'A';
}
const vector<int> dy = {-1,0,1,0};
const vector<int> dx = {0,1,0,-1};
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    int H,W; cin >> H >> W;
    vector<string> field(H);
    for(string& s : field){
        cin >> s;
    }

    set<tuple<int,int> > should_press;
    for(int y=0;y<H;y++){
        for(int x=0;x<W;x++){
            vector<int> v(26);
            for(int i=0;i<4;i++){
                for(int k=1;k<max(H,W);k++){
                    int ny = y + k * dy[i],
                        nx = x + k * dx[i];
                    if(ny < 0 or ny >= H or nx < 0 or nx >= W) break;
                    const char here = field[ny][nx];
                    if(here != dot){
                        v[to_index(here)]++;
                        // break;
                    }
                }
            }
            if(*max_element(all(v)) >= 2){
                should_press.insert(make_tuple(y,x));
            }
        }
    }

    // dump(should_press);
    bool cont = true;
    int ret = 0;
    while(cont){
        cont = false;
        for(auto t : should_press){
            int y = get<0>(t),
                x = get<1>(t);
            if(field[y][x] != dot) continue;
            vector<tuple<int,int> > tiles;
            for(int i=0;i<4;i++){
                for(int k=1;k<max(H,W);k++){
                    int ny = y + k * dy[i],
                        nx = x + k * dx[i];
                    if(ny < 0 or ny >= H or nx < 0 or nx >= W) break;
                    const char here = field[ny][nx];
                    if(here != dot){
                        tiles.emplace_back(ny,nx);
                        break;
                    }
                }
            }
            for(int i=0;i<tiles.size();i++){
                for(int j=i+1;j<tiles.size();j++){
                    char& ic = field[get<0>(tiles[i])][get<1>(tiles[i])];
                    char& ij = field[get<0>(tiles[j])][get<1>(tiles[j])];
                    if(ic == ij){
                        ic = dot;
                        ij = dot;
                        ret+=2;
                        cont = true;
                        goto end;
                    }
                }
            }
        end:{}
        }
    }
    cout << ret << endl;
    return 0;
}

Measured memory_bytes: 1492