#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cfloat>
#include <map>
#include <utility>
#include <set>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <sstream>
#include <complex>
#include <stack>
#include <cstring>
#include <queue>
using namespace std;
static const double EPS = 1e-10;
typedef long long ll;
#define rep(i,n) for(int i=0;i<(int)n;i++)
#define rev(i,n) for(int i=(int)n-1;i>=0;i--)
#define all(a) a.begin(),a.end()
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define SS stringstream
#define DBG1(a) rep(_X,sz(a)){printf("%d ",a[_X]);}puts("");
#define DBG2(a) rep(_X,sz(a)){rep(_Y,sz(a[_X]))printf("%d ",a[_X][_Y]);puts("");}
#define bitcount(b) __builtin_popcount(b)
#define EACH(i,c) for(typeof((c).begin()) i=(c).begin(); i!=(c).end(); ++i)

#define delete(a,n) a.erase(remove(all(a),n),a.end())
template<typename T, typename S> vector<T>& operator<<(vector<T>& a, S b) { a.push_back(b); return a; }
template<typename T> void operator>>(vector<T>& a, int b) {while(b--)if(!a.empty())a.pop_back();}
bool isprime(int n){ if(n<2)return false;  for(int i=2;i*i<=n;i++)if(n%i==0)return false;  return true;} 
ll b_pow(ll x,ll n){return n ? n==1 ? x: b_pow(x,n>>1)*b_pow(x,(n>>1)+(n&1)) : 1;}
string itos(int n){stringstream ss;ss << n;return ss.str();}

//long longÉ«ðÂ¯Ä
int main(){
	int w,h;
	cin >> h >> w;
	vector< pair<int,int> > v[26];
	rep(i,h){
		rep(j,w){
			char c; cin >> c;
			if(c != '.') v[c-'A'].push_back(mp(j,i));
		}
	}
	vector<int> x_c,y_c;
	rep(i,26)rep(j,v[i].size()) x_c.push_back(v[i][j].first) , x_c.push_back(v[i][j].first-1), x_c.push_back(v[i][j].first+1);
	rep(i,26)rep(j,v[i].size()) y_c.push_back(v[i][j].second), y_c.push_back(v[i][j].second-1), y_c.push_back(v[i][j].second+1);
	sort(all(x_c)); x_c.erase( unique(all(x_c)) , x_c.end());
	sort(all(y_c)); y_c.erase( unique(all(y_c)) , y_c.end());
	rep(i,26){
		rep(j,v[i].size()){
			v[i][j].first  = lower_bound(all(x_c),v[i][j].first)  - x_c.begin();
			v[i][j].second = lower_bound(all(y_c),v[i][j].second) - y_c.begin();
		}
	}
	w = x_c.size();
	h = y_c.size();
	set< pair<int,int> > ex_x[200] , ex_y[200];
	rep(i,200){
		ex_x[i].insert(mp(-1000,-1));
		ex_x[i].insert(mp(1000,-1));
		ex_y[i].insert(mp(-1000,-1));
		ex_y[i].insert(mp(1000,-1));
	}
	char b[200][200]={0};
	rep(i,26){
		rep(j,v[i].size()){
			int x = v[i][j].first;
			int y = v[i][j].second;
			b[y][x] = 'A'+i;
			ex_x[y].insert(mp(x,i));
			ex_y[x].insert(mp(y,i));
		}
	}
	
	int answer = 0;
	rep(___,60){
		rep(i,h)rep(j,w){
			if(b[i][j] == 0){
				set< pair<int,int> >::iterator it[4];
				it[0] = ex_x[i].lower_bound(mp(j,0)); it[0]--;
				it[1] = ex_x[i].lower_bound(mp(j,0));
				it[2] = ex_y[j].lower_bound(mp(i,0)); it[2]--;
				it[3] = ex_y[j].lower_bound(mp(i,0));
				vector< pair<int,int> > remover[26];
				rep(A,4){
					if(it[A]->second != -1){
						if(A < 2){
							remover[it[A]->second].push_back(mp(it[A]->first,i));
						}else{
							remover[it[A]->second].push_back(mp(j,it[A]->first));
						}
					}
				}
				rep(A,26){
					if(remover[A].size() == 2){
						rep(B,2){
							int x = remover[A][B].first;
							int y = remover[A][B].second;
							ex_x[y].erase(mp(x,A));
							ex_y[x].erase(mp(y,A));
							b[y][x] = 0;
							answer ++;
						}
					}
				}
			}
		}
	}
	cout << answer << endl;
}

Measured memory_bytes: 964