#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<complex>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<utility>
using namespace std;
typedef long long ll;
typedef unsigned long long ul;
typedef unsigned int ui;
const ll mod = 1000000007;
typedef long double ld;
typedef complex<ld> Point;
const ll INF = mod * mod;
typedef pair<int, int> P;
#define stop char nyaa;cin>>nyaa;
#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
const ld eps = 1e-6;
const ld pi = acos(-1.0);
typedef pair<ld, ld> LDP;
typedef pair<ll, ll> LP;

int h, w;
char mp[51][51];

bool visted[51][51][51][51];
int ans[51][51][51][51];

bool ismalf(char t) {
	if ('a' <= t && t <= 'z')return true;
	return false;
}
bool isMalf(char t) {
	if ('A' <= t && t <= 'Z')return true;
	return false;
}
bool corres(char s, char t) {
	if (ismalf(s) && isMalf(t)) {
		int l = s - 'a', r = t - 'A';
		return l == r;
	}
	return false;
}



int dx[2] = { 1,0 };
int dy[2] = { 0,1 };

vector<P> alf[26];
vector<P> Alf[26];

int dfs(int x, int y, int z, int u) {
	if (x < 0 || y < 0 || x >= h || y >= w || z < 0 || z >= h || u < 0 || u >= w)return -mod;
	if (x == z && y == u) {
		if (mp[x][y] == '#')return -mod;
		else return 0;
	}
	if (z < x || u < y)return -mod;
	if (mp[x][y] == '#' || mp[z][u] == '#')return -mod;
	if (visted[x][y][z][u])return ans[x][y][z][u];
	visted[x][y][z][u] = true;
	//cout << x << " " << z << endl;
	char le = mp[x][y], ri = mp[z][u];
	if (z - x + u - y == 1) {
		if (corres(le, ri)) {
			return ans[x][y][z][u] = 1;
		}
		else {
			return ans[x][y][z][u] = 0;
		}
	}
	int ret = -mod;
	if (corres(le, ri)) {
		//cout << le << " " << ri << endl;
		rep(i, 2) {
			rep(j, 2) {
				int nx = x + dx[i], ny = y + dy[i];
				int nz = z - dx[j], nu = u - dy[j];
				ret = max(ret, dfs(nx, ny, nz, nu));
			}
		}
		ret++;
	}
	rep(i, 2) {
		int nx = x + dx[i], ny = y + dy[i];
		ret = max(ret, dfs(nx, ny, z, u));
	}
	if (ismalf(le)) {
		int t = le - 'a';
		rep(i, Alf[t].size()) {
			int mx = Alf[t][i].first, my = Alf[t][i].second;
			if (mx<x || mx>z || my<y || my>u)continue;
			rep(k, 2) {
				int l = dfs(x, y, mx, my);
				int r = dfs(mx + dx[k], my + dy[k], z, u);
				ret = max(ret, l + r);
			}
		}
	}
	return ans[x][y][z][u] = ret;

}

void solve() {
	rep(i, 26) {
		alf[i].clear();
	}
	rep(i, 26) {
		Alf[i].clear();
	}
	rep(i, h) {
		rep(j, w) {
			cin >> mp[i][j];
			char t = mp[i][j];
			if (ismalf(t)) {
				alf[(int)(t - 'a')].push_back({ i,j });
			}
			else if (isMalf(t)) {
				Alf[(int)(t - 'A')].push_back({ i,j });
			}
			rep(k, h) {
				rep(l, w) {
					ans[i][j][k][l] = 0;
					visted[i][j][k][l] = false;
				}
			}
		}
	}
	int out = dfs(0, 0, h - 1, w - 1);
	if (out < 0)out = -1;
	cout << out << endl;
}

void generate() {
	cout << "50 50" << endl;
	rep(i, 50) {
		rep(j, 50) {
			char z = '.';
			if (i % 10 == 0) {
				z = 'A' + ((i + j) % 26);
			}
			else if (i % 10 == 1) {
				z = 'a' + ((i + j) % 26);
			}
			cout << z;
		}
		cout << endl;
	}
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	//generate();
	while (cin >> h >> w, h) {
		solve();
	}
	//stop
	return 0;
}


Measured memory_bytes: 35192