// tsukasa_diary's programing contest code template
#include <bits/stdc++.h>
using namespace std;
// define
#define for_(i,a,b) for(int i=a;i<b;++i)
#define for_rev(i,a,b) for(int i=a;i>=b;--i)
#define allof(a) a.begin(),a.end()
#define minit(a,b) memset(a,b,sizeof(a))
#define size_of(a) (int)a.size()
// typedef
typedef long long lint;
typedef double Double;
typedef pair<int, int> pii;
//
typedef vector< int > Array;
typedef vector< Array > Matrix;
typedef vector< Double > DArray;
typedef vector< DArray > DMatrix;
typedef vector< string > SArray;
typedef vector< pii > PArray;
// popcount
inline int POPCNT(int _x) { return __builtin_popcount(_x); }
inline int POPCNT(lint _x) { return __builtin_popcountll(_x); }
// inf
const int iINF = 1L << 30;
const lint lINF = 1LL << 60;
// eps
Double EPS = 1e-9;
// in range
inline bool in_range(int _v, int _mx, int _mi) { return _mi <= _v && _v < _mx; }
inline bool in_range(Double _v, Double _mi, Double _mx) { return -EPS < _v - _mi && _v - _mx < EPS; }
inline bool in_range(int _x, int _y, int _W, int _H) { return 0 <= _x && _x < _W && 0 <= _y && _y < _H; }
// neighbor clockwise
const int DX[4] = {0,1,0,-1}, DY[4] = {-1,0,1,0};
const int DX_[8] = {0,1,1,1,0,-1,-1,-1}, DY_[8] = {-1,-1,0,1,1,1,0,-1};
// variable update
inline void modAdd(int& _a, int _b, int _m) { _a = (_a + _b) % _m; }
inline void modAdd(lint& _a, lint _b, lint _m) { _a = (_a + _b) % _m; }
inline void minUpdate(int& _a, int _b) { _a = min(_a, _b); }
inline void minUpdate(lint& _a, lint _b) { _a = min(_a, _b); }
inline void maxUpdate(int& _a, int _b) { _a = max(_a, _b); }
inline void maxUpdate(lint& _a, lint _b) { _a = max(_a, _b); }

int H, W;
string grid[555];

struct Point {
	int x, y;
	Point(int _x, int _y) : x(_x), y(_y) {}
};

vector< vector< Point > > vvp;

bool is_able(int x, int y, int gx, int gy, int d, bool flp) {
	if (!in_range(x, y, W, H)) return false;
	if (grid[y][x] != '.' && grid[y][x] != grid[gy][gx]) return false;
	if (x == gx && y == gy) return true;
	
	if (is_able(x + DX[d], y + DY[d], gx, gy, d, flp)) return true;
	if (flp && is_able(x + DX[(d + 1) % 4], y + DY[(d + 1) % 4], gx, gy, (d + 1) % 4, false)) return true;
	if (flp && is_able(x + DX[(d + 3) % 4], y + DY[(d + 3) % 4], gx, gy, (d + 3) % 4, false)) return true;
	
	return false;
}

void solve() {
	vvp.assign(26, vector< Point >());
	
	for_(y,0,H) for_(x,0,W) {
		if (grid[y][x] != '.') {
			int id = grid[y][x] - 'A';
			vvp[id].push_back(Point(x, y));
		}
	}
	
	int ans = 0;
	
	for_(rep,0,26) {
		for_(i,0,26) {
			if (size_of(vvp[i]) == 0) continue;
			Point p1 = vvp[i][0], p2 = vvp[i][1];
			
			if (p1.x == p2.x && p1.y + 1 == p2.y) continue;
			if (p1.y == p2.y && p1.x + 1 == p2.x) continue;
			
			bool flag = false;
			for_(d,0,4) flag |= is_able(p1.x, p1.y, p2.x, p2.y, d, true);

			if (flag) {
				grid[p1.y][p1.x] = grid[p2.y][p2.x] = '.';
				ans += 2;
				vvp[i].clear();
			}
		}
	}
	
	cout << ans << endl;
}

int main() {
	cin >> H >> W;
	for_(i,0,H) cin >> grid[i];
	solve();	
	return 0;
}