#include "iostream"
#include "climits"
#include "list"
#include "queue"
#include "stack"
#include "set"
#include "functional"
#include "algorithm"
#include "string"
#include "map"
#include "unordered_map"
#include "unordered_set"
#include "iomanip"
#include "cmath"
#include "random"
#include "bitset"
#include "cstdio"
#include "numeric"
#include "cassert"
#include "ctime"

using namespace std;

//constexpr long long int MOD = 1000000007;
//constexpr int MOD = 1000000007;
//constexpr int MOD = 998244353;
constexpr long long int MOD = 998244353;
constexpr double EPS = 1e-9;

//int N, M, K, H, W, L, R;
long long int N, M, K, H, W, L, R;

vector<vector<int>>to(vector<string>&s, int y, int x) {
	vector<vector<int>>ret(s.size(), vector<int>(s[0].size()));
	ret[y][x] = 1;
	for (int i = 0; i < H; i++) {
		for (int j = 0; j < W; j++) {
			if (i + 1 < H) {
				if (s[i + 1][j] != '#')ret[i + 1][j] |= ret[i][j];
			}
			if (j + 1 < W) {
				if (s[i][j + 1] != '#')ret[i][j + 1] |= ret[i][j];
			}
		}
	}
	return ret;
}

vector<vector<int>>rev(vector<string>&s, int y, int x) {
	vector<vector<int>>ret(s.size(), vector<int>(s[0].size()));
	ret[y][x] = 1;
	for (int i = H - 1; i >= 0; i--) {
		for (int j = W - 1; j >= 0; j--) {
			if (i) {
				if (s[i - 1][j] != '#')ret[i - 1][j] |= ret[i][j];
			}
			if (j) {
				if (s[i][j - 1] != '#')ret[i][j - 1] |= ret[i][j];
			}
		}
	}
	return ret;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);

	vector<int>o;
	while (cin >> H >> W, H) {
		vector<string>s(H);
		for (auto &i : s)cin >> i;
		int ret = -1;
		auto st = to(s, 0, 0);
		auto gl = rev(s, H - 1, W - 1);
		if (st.back().back())ret = 0;
		vector<pair<int, int>>l;
		vector<pair<int, int>>r;
		for (int i = 0; i < H; i++) {
			for (int j = 0; j < W; j++) {
				if (s[i][j] >= 'a'&&s[i][j] <= 'z'&&st[i][j] && gl[i][j])l.emplace_back(i, j);
				if (s[i][j] >= 'A'&&s[i][j] <= 'Z'&&st[i][j] && gl[i][j])r.emplace_back(i, j);
			}
		}
		vector < pair<pair<int, int>, pair<int, int>>>p;
		for (int i = 0; i < l.size(); i++) {
			auto t = to(s, l[i].first, l[i].second);
			for (int j = r.size() - 1; j >= 0; j--) {
				if (s[l[i].first][l[i].second] - 'a' == s[r[j].first][r[j].second] - 'A'&&t[r[j].first][r[j].second]) {
					p.push_back({ l[i], r[j] });
				}
			}
		}
		vector<int>num(p.size(),1);
		vector<vector<vector<int>>>ed_to(p.size());
		for (int i = 0; i < p.size(); i++) {
			ed_to[i] = to(s, p[i].second.first, p[i].second.second);
		}
		for (int i = p.size() - 1; i >= 0; i--) {
			auto fst = to(s, p[i].first.first, p[i].first.second);
			auto snd = rev(s, p[i].second.first, p[i].second.second);
			vector<int>dp(p.size(), 0);
			for (int j = i + 1; j < p.size(); j++) {
				if (p[i].first == p[j].first)continue;
				if (p[i].second == p[j].second)continue;
				if (!fst[p[j].first.first][p[j].first.second] || !snd[p[j].second.first][p[j].second.second]) continue;
				dp[j] = num[j];
				for (int k = i + 1; k < j; k++) {
					if (p[i].first == p[k].first)continue;
					if (p[i].second == p[k].second)continue;
					if (p[j].first == p[k].first)continue;
					if (p[j].second == p[k].second)continue;
					if (ed_to[k][p[j].first.first][p[j].first.second]) {
						dp[j]= max(dp[j], dp[k] + num[j]);
					}
				}
			}
			num[i] = *max_element(dp.begin(), dp.end()) + 1;
		}
		auto dp = num;
		for (int i = 0; i < p.size(); i++) {
			for (int j = i + 1; j < p.size(); j++) {
				if (p[i].first == p[j].first)continue;
				if (p[i].second == p[j].second)continue;
				if (ed_to[i][p[j].first.first][p[j].first.second]) {
					dp[j] = max(dp[j], num[j] + dp[i]);
				}
			}
		}
		for (auto i : dp) {
			ret = max(ret, i);
		}
		o.push_back(ret);
	}
	for (auto i : o)cout << i << endl;
}


Measured memory_bytes: 9784