Problem:
Problem Statement
There is a maze which can be described as a
W \times H
grid.
The upper-left cell is denoted as (1, 1), and the lower-right cell is
(W, H)
.
You  are now at the cell (1, 1) and  have to  go to the cell
(W, H)
.
However, you can only move to the right adjacent cell or to the lower adjacent cell.
The following figure is an example of a maze.
...#......
a###.#####
.bc...A...
##.#C#d#.#
.#B#.#.###
.#...#e.D.
.#A..###.#
..e.c#..E.
####d###.#
#....#.#.#
##E...d.C.
In the maze, some cells are free (denoted by
.
) and some cells are occupied by rocks (denoted by
#
), where you cannot enter.
Also there are jewels (denoted by lowercase alphabets) in some of the free cells  and holes to place jewels (denoted by uppercase alphabets).
Different alphabets  correspond to different types of jewels, i.e. a cell denoted by
a
contains a jewel of type A, and a cell denoted by
A
contains a hole to place a jewel of type A.
It is said that, when we place a jewel to a corresponding hole, something happy will happen.
At the cells with jewels, you can choose whether you pick a jewel or not.
Similarly, at the cells with holes, you can choose whether you place a jewel you have or not.
Initially you do not have any jewels.
You have a very big bag, so you can bring arbitrarily many jewels.
However, your bag is a stack, that is, you can only place the jewel that you  picked up last.
On the way from cell (1, 1) to cell
(W, H)
, how many jewels can you place to correct holes?
Input
The input  contains a sequence of datasets.
The end of the input is indicated by a line containing two zeroes.
Each dataset is formatted as follows.
H
W
C_{11}
C_{12}
...
C_{1W}
C_{21}
C_{22}
...
C_{2W}
...
C_{H1}
C_{H2}
...
C_{HW}
Here,
H
and
W
are the height and width of the grid.
You may assume
1 \leq W, H \leq 50
.
The rest of the datasets consists of
H
lines, each of which is composed of
W
letters.
Each letter
C_{ij}
specifies the type of the cell
(i, j)
as described before.
It is guaranteed that
C_{11}
and
C_{WH}
are never
#
.
You may also assume that each lowercase or uppercase alphabet appear at most 10 times in each dataset.
Output
For each dataset, output the maximum number of  jewels that you can place to corresponding holes.
If you cannot reach the cell
(W, H)
, output -1.
Sample Input
3 3
ac#
b#C
.BA
3 3
aaZ
a#Z
aZZ
3 3
..#
.#.
#..
1 50
abcdefghijklmnopqrstuvwxyYXWVUTSRQPONMLKJIHGFEDCBA
1 50
aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyY
1 50
abcdefghijklmnopqrstuvwxyABCDEFGHIJKLMNOPQRSTUVWXY
1 50
aaaaaaaaaabbbbbbbbbbcccccCCCCCBBBBBBBBBBAAAAAAAAAA
10 10
...#......
a###.#####
.bc...A...
##.#C#d#.#
.#B#.#.###
.#...#e.D.
.#A..###.#
..e.c#..E.
####d###.#
##E...D.C.
0 0
Output for the Sample Input
2
0
-1
25
25
1
25
4

Submitted solutions:

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define N 55

const int INF = (int)1e9;

bool adj[N][N][N][N];
int f[N][N][N][N];
char mat[N][N];
int n, m;
vector<pair<int, int> > vec[26];

bool ok(int x, int y) {
	return x >= 0 && x < n && y >= 0 && y < m && mat[x][y] != '#';
}

int dfs(int lx, int ly, int rx, int ry) {
	if (f[lx][ly][rx][ry] != -1) return f[lx][ly][rx][ry];
	if (lx > rx || ly > ry) {
		return f[lx][ly][rx][ry] = 0;
	}
	if (!adj[lx][ly][rx][ry]) {
		return f[lx][ly][rx][ry] = -INF;
	}		

	int res = 0;

	if (mat[lx][ly] >= 'a' && mat[lx][ly] <= 'z') {
		int id = mat[lx][ly] - 'a';
		
		for (int i = 0; i < vec[id].size(); i++) {
			int x = vec[id][i].first, y = vec[id][i].second;

			if (!adj[lx][ly][x][y]) continue;

			if (x >= lx && x <= rx && y >= ly && y <= ry) {
				if (ok(lx + 1, ly)) {
					if (ok(x - 1, y)) {
						res = max(res, dfs(lx + 1, ly, x - 1, y) + 1 + dfs(x, y, rx, ry));
					}
					if (ok(x, y - 1)) {
						res = max(res, dfs(lx + 1, ly, x, y - 1) + 1 + dfs(x, y, rx, ry));
					}
				}

				if (ok(lx, ly + 1)) {
					if (ok(x - 1, y)) {
						res = max(res, dfs(lx, ly + 1, x - 1, y) + 1 + dfs(x, y, rx, ry));
					}
					if (ok(x, y - 1)) {
						res = max(res, dfs(lx, ly + 1, x, y - 1) + 1 + dfs(x, y, rx, ry));
					}
				}			
			}			

			/*
			if (lx == 1 && ly == 1 && rx == 4 && ry == 4) {
				cout << "here" << endl;
				cout << res << endl;
				cout << id << endl;
				cout << x << ' ' << y << endl;
			}*/
		}		


	}


	if (ok(lx + 1, ly)) {
		res = max(res, dfs(lx + 1, ly, rx, ry));		
	}	
	if (ok(lx, ly + 1)) {
		res = max(res, dfs(lx, ly + 1, rx, ry));
	}

	return f[lx][ly][rx][ry] = res;
}

int main() {
	while (~scanf("%d%d", &n, &m)) {
		if (n + m == 0) break;

		for (int i = 0; i < 26; i++) {
			vec[i].clear();
		}

		for (int i = 0; i < n; i++) {
			scanf("%s", mat[i]);
			for (int j = 0; j < m; j++) {
				if (mat[i][j] >= 'A' && mat[i][j] <= 'Z') {
					int id = mat[i][j] - 'A';
					vec[id].push_back(make_pair(i, j));
				}
			}
		}

		memset(adj, 0, sizeof(adj));
		memset(f, -1, sizeof(f));

		for (int i = n - 1; i >= 0; i--) {
			for (int j = m - 1; j >= 0; j--) {

				if (mat[i][j] == '#') continue;

				for (int x = i; x < n; x++) {
					for (int y = j; y < m; y++) {
						if (ok(i + 1, j)) {
							adj[i][j][x][y] |= adj[i + 1][j][x][y];
						}
						if (ok(i, j + 1)) {
							adj[i][j][x][y] |= adj[i][j + 1][x][y];
						}
					}
				}
				
				adj[i][j][i][j] = 1;		
			}
		}	

		int res = dfs(0, 0, n - 1, m - 1);

		/*

		for (int x = 0; x < n; x++) {
			for (int y = 0; y < m; y++) {
				for (int i = x; i < n; i++) {
					for (int j = y; j < m; j++) {
						if (f[x][y][i][j] == 1) {
							cout << "yes" << endl;
							cout << x << ' ' << y << endl;
							cout << i << ' ' << j << endl;
						}
					}
				}
			}
		}*/

		if (res == -INF)
			puts("-1");
		else
			printf("%d\n", res);
	}
	return 0;
}
accuracy: 1
time_ms: 110
memory_kb: 48204
final_rank: 12

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i,n) for(ll i=0;i<n;i++)
#define all(A) A.begin(),A.end()
const ll mod = 1e9 + 7;

void solve(int H, int W) {
    vector<string> S(H);
    rep(h, H)cin >> S[h];

    ll an=-1;
    vector<vector<vector<vector<int>>>> DP(H, vector<vector<vector<int>>>(W, vector<vector<int>>(H, vector<int>(W, -1e9))));
    rep(h, H)rep(w, W) {
        if (S[h][w] != '#')DP[h][w][h][w] = 0;
    }
    vector<int> dx2 = { 1,0 }, dy2 = { 0,1 }, dx = { -1,0 }, dy = { 0,-1 };
    rep(d, 101)rep(h, H)rep(w, W) {
        if (d == 0)continue;
        if (S[h][w] == '#')continue;
        rep(dh,d+1){
            int dw=d-dh;
            int h2=h-dh;
            int w2=w-dw;
            if(h2<0||w2<0)continue;
            if (S[h2][w2] == '#')continue;
            if (abs(h2 - h) + abs(w2 - w) != d)continue;
            for(int h3=h2;h3<=h;h3++){
                for(int w3=w2;w3<=w;w3++){
                    if(S[h3][w3]=='#')continue;
                    DP[h2][w2][h][w]=max(DP[h2][w2][h][w],DP[h2][w2][h3][w3]+DP[h3][w3][h][w]);
                }
            }
            if ('A' <= S[h][w] && S[h][w] <= 'Z' && S[h][w] - 'A' + 'a' == S[h2][w2]) {
                if (d == 1) {
                    DP[h2][w2][h][w] = 1;
                }
                else {
                    rep(t, 2)rep(k, 2) {
                        int nh = h + dy[t];
                        int nw = w + dx[t];
                        int nh2 = h2 + dy2[k];
                        int nw2 = w2 + dx2[k];
                        if (nh < 0 || nw < 0 || nh2 < 0 || nw2 < 0)continue;
                        if (nh >= H || nw >= W || nh2 >= H || nw2 >= W)continue;
                        if (S[nh][nw] == '#' || S[nh2][nw2] == '#')continue;
                        DP[h2][w2][h][w] = max(DP[h2][w2][h][w], DP[nh2][nw2][nh][nw] + 1);
                    }
                }
            }
            rep(t, 2) {
                int nh = h + dy[t];
                int nw = w + dx[t];

                if (nh < 0 || nw < 0)continue;
                if (nh >= H || nw >= W)continue;
                if (S[nh][nw] == '#')continue;
                DP[h2][w2][h][w] = max(DP[h2][w2][h][w], DP[h2][w2][nh][nw]);
            }
            rep(t, 2) {
                int nh2 = h2 + dy2[t];
                int nw2 = w2 + dx2[t];

                if (nh2 < 0 || nw2 < 0)continue;
                if (nh2 >= H || nw2 >= W)continue;
                if (S[nh2][nw2] == '#')continue;
                DP[h2][w2][h][w] = max(DP[h2][w2][h][w], DP[nh2][nw2][h][w]);
            }

        }

    }
    vector<vector<ll>> NDP(H,vector<ll>(W,-1e18));
    NDP[0][0]=0;
    rep(h,H)rep(w,W){
        if(S[h][w]=='#')continue;
        an=max(an,NDP[h][w]+DP[h][w][H-1][W-1]);
        rep(h2,H)rep(w2,W){
            rep(t,2){
                int nh2 = h2 + dy2[t];
                int nw2 = w2 + dx2[t];

                if (nh2 < 0 || nw2 < 0)continue;
                if (nh2 >= H || nw2 >= W)continue;
                if (S[nh2][nw2] == '#')continue;
                NDP[nh2][nw2]=max(NDP[nh2][nw2],DP[h][w][h2][w2]+NDP[h][w]);
            }
        }
        rep(t,2){
            int nh = h + dy2[t];
            int nw = w + dx2[t];

            if (nh < 0 || nw < 0)continue;
            if (nh >= H || nw >= W)continue;
            if (S[nh][nw] == '#')continue;
            NDP[nh][nw]=max(NDP[nh][nw],NDP[h][w]);
        }
    }
    NDP[H - 1][W - 1]=max(NDP[H - 1][W - 1],an);
    if(NDP[H-1][W-1]<0)cout<<-1<<endl;
    else cout << NDP[H - 1][W - 1] << endl;
}

int main() {

    cin.tie(nullptr);
    ios::sync_with_stdio(false);

    int H, W;
    while (cin >> H >> W, H + W != 0)
        solve(H, W);



}
accuracy: 1
time_ms: 7440
memory_kb: 32104
final_rank: 19

code_snippet: #include <iostream>
#include <vector>
#include <cstring>
using namespace std;

const int inf = 1 << 30;

int h, w;
vector<string> s;
vector<vector<vector<vector<int>>>> memo;

int calc(int x1, int y1, int x2, int y2) {
  if (!(0 <= x1 && x1 <= x2 && x2 < h && 0 <= y1 && y1 <= y2 && y2 < w && s[x1][y1] != '#' && s[x2][y2] != '#')) {
    return -inf;
  }
  if (x1 == x2 && y1 == y2) {
    return 0;
  }
  if (memo[x1][y1][x2][y2] != -1) {
    return memo[x1][y1][x2][y2];
  }
  int c = -inf;
  if (s[x1][y1] - 'a' == s[x2][y2] - 'A' && 'a' <= s[x1][y1] && s[x1][y1] <='z') {
    if (abs(x1 - x2) + abs(y1 - y2) == 1) {
      return 1;
    }
    vector<pair<int,int>> directions = {{0, 1}, {1, 0}};
    for (auto dir1 : directions) {
      for (auto dir2 : directions) {
        auto [dx1, dy1] = dir1;
        auto [dx2, dy2] = dir2;
        c = max(c, calc(x1 + dx1, y1 + dy1, x2 - dx2, y2 - dy2) + 1);
      }
    }
  }

  c = max(c, calc(x1 + 1, y1, x2, y2));
  c = max(c, calc(x1, y1 + 1, x2, y2));
  c = max(c, calc(x1, y1, x2 - 1, y2));
  c = max(c, calc(x1, y1, x2, y2 - 1));
  if('a' <= s[x1][y1] && s[x1][y1] <='z' && 'A' <= s[x2][y2] && s[x2][y2] <='Z'){
    for (int x = x1; x <= x2; ++x) {
      for (int y = y1; y <= y2; ++y) {
        if (x == x2 && y == y2) {
          continue;
        }
        c = max(c, calc(x1, y1, x, y) + calc(x + 1, y, x2, y2));
        c = max(c, calc(x1, y1, x, y) + calc(x, y + 1, x2, y2));
      }
    }
  }
  memo[x1][y1][x2][y2] = c;
  return c;
}

int main() {
  while (true) {
    cin >> h >> w;
    if (h == w && w == 0) {
      break;
    }
    s.clear();
    s.resize(h);
    for (int i = 0; i < h; ++i) {
      cin >> s[i];
    }
    memo.clear();
    memo.resize(h, vector<vector<vector<int>>>(w, vector<vector<int>>(h, vector<int>(w, -1))));
    int ans = calc(0, 0, h - 1, w - 1);
    cout << max(ans, -1) << endl;
  }
  return 0;
}
// コピペした
accuracy: 1
time_ms: 340
memory_kb: 32812
final_rank: 9

code_snippet: /*	author:  Kite_kuma
	created: 2021.09.06 20:00:41 */

// #ifdef LOCAL
// #define _GLIBCXX_DEBUG
// #endif
#include <bits/stdc++.h>
using namespace std;

#pragma region macros

#define foa(s, v) for(auto &s : v)
#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()
#define popcnt(n) __builtin_popcountll((long long)n)

#define REPname(a, b, c, d, e, ...) e
#define rep(...) REPname(__VA_ARGS__, REP3, REP2, REP1, REP0)(__VA_ARGS__)
#define REP0(x) for(int Counter_in_rep_macro = 0; Counter_in_rep_macro < (x); ++Counter_in_rep_macro)
#define REP1(i, x) for(int i = 0; i < (x); ++i)
#define REP2(i, l, r) for(int i = (l); i < (r); ++i)
#define REP3(i, l, r, c) for(int i = (l); i < (r); i += (c))

#define DREPname(a, b, c, d, e, ...) e
#define drep(...) DREPname(__VA_ARGS__, DREP3, DREP2, DREP1)(__VA_ARGS__)
#define DREP1(i, x) for(int i = (x)-1; i >= 0; --i)
#define DREP2(i, l, r) for(int i = (r)-1; i >= (l); --i)
#define DREP3(i, l, r, c) for(int i = (r)-1; i >= (l); i -= (c))

#pragma endregion

#pragma region aliases

using ll = long long;
using ld = long double;
using ull = unsigned long long;
using vi = std::vector<int>;
using vvi = std::vector<std::vector<int>>;
using vvvi = std::vector<std::vector<std::vector<int>>>;
using vll = std::vector<ll>;
using vvll = std::vector<vll>;
using vvvll = std::vector<vvll>;
using pii = std::pair<int, int>;
using pll = std::pair<long long, long long>;
template <class T = ll>
using V = std::vector<T>;
template <class T = ll>
using VV = V<V<T>>;
template <class T = ll>
using VVV = V<V<V<T>>>;
template <class T = ll>
using pqup = std::priority_queue<T, std::vector<T>, std::greater<T>>;
template <class T = ll>
using pqdn = std::priority_queue<T>;

#pragma endregion

#pragma region constants

const int inf = 1e9;
const long long INF = 1e18;
const long double pi = acos(-1);
const char dl = '\n';
const char sp = ' ';
int dx[8] = {1, 0, -1, 0, 1, -1, -1, 1};
int dy[8] = {0, 1, 0, -1, 1, 1, -1, -1};

const int mod_1000000007 = 1000000007;
const int mod_998244353 = 998244353;

#pragma endregion

#pragma region basic_operation

template <class T0, class T1, class T2>
inline bool in_range(T0 x, T1 lef, T2 rig) {
	return ((lef <= x) && (x < rig));
}

template <class T>
inline bool chmin(T &a, T b) {
	if(a > b) {
		a = b;
		return true;
	}
	return false;
}
template <class T>
inline bool chmax(T &a, T b) {
	if(a < b) {
		a = b;
		return true;
	}
	return false;
}

void Yes(bool f = 1) { std::cout << (f ? "Yes" : "No") << '\n'; }
void No() { std::cout << "No\n"; }
void YES(bool f = 1) { std::cout << (f ? "YES" : "NO") << '\n'; }
void NO() { std::cout << "NO\n"; }

template <class T>
void drop(T answer) {
	std::cout << answer << '\n';
	exit(0);
}

void err(bool flag = true) {
	if(!flag) return;
	std::cout << -1 << '\n';
	exit(0);
}

template <class T>
void vout(std::vector<T> const &v, bool vertically = 0) {
	if(vertically) {
		for(auto const &a : v) {
			std::cout << a << '\n';
		}
		return;
	}
	for(auto it = v.begin(); it != v.end(); it++) {
		std::cout << (*it);
		if(it != v.end() - 1) {
			std::cout << ' ';
		}
	}
	std::cout << '\n';
	return;
}

inline void print() { std::cout << '\n'; }
template <class T>
inline void print(T x) {
	std::cout << x << '\n';
	return;
}
template <typename Head, typename... Tail>
void print(Head H, Tail... T) {
	std::cout << H << " ";
	print(T...);
}

template <class T>
void add(std::vector<T> &v, T val) {
	for(auto &a : v) a += val;
	return;
}

template <class T>
T dup(T a, T b) {
	assert(b != 0);
	return (a + b - 1) / b;
}

template <class T>
T greatest_lower_multiple(T x, T d) {
	if(d == 0) return 0;
	if(d < 0) d *= -1;
	T y = x % d;
	if(y < 0) y += d;
	return x - y;
}

template <class T>
T least_upper_multiple(T x, T d) {
	return -greatest_lower_multiple(-x, d);
}

long long POW(long long a, long long n) {
	long long res = 1;
	while(n > 0) {
		if(n & 1) res = res * a;
		a = a * a;
		n >>= 1;
	}
	return res;
}

long long modpow(long long a, long long n, long long mod) {	 // a^n mod
	assert(n >= 0 && mod);
	if(mod == 1) return 0LL;
	long long res = 1;
	a %= mod;
	while(n > 0) {
		if(n & 1) res = res * a % mod;
		a = a * a % mod;
		n >>= 1;
	}
	return res < 0 ? res + mod : res;
}

// return x which satisfies a * x % mod == gcd(a, mod)
// not (mod | a)
long long modinv(long long a, long long mod) {
	long long b = mod, u = 1, v = 0;
	while(b) {
		long long t = a / b;
		a -= t * b;
		std::swap(a, b);
		u -= t * v;
		std::swap(u, v);
	}
	u %= mod;
	if(u < 0) u += mod;
	return u;
}

template <class T>
int lb(const std::vector<T> &a, const T x) {
	return std::distance(a.begin(), std::lower_bound(a.begin(), a.end(), x));
}
template <class T>
int ub(const std::vector<T> &a, const T x) {
	return std::distance(a.begin(), std::upper_bound(a.begin(), a.end(), x));
}
template <class T>
void unq_sort(std::vector<T> &a) {
	std::sort(a.begin(), a.end());
	a.erase(std::unique(a.begin(), a.end()), a.end());
}
template <class T>
std::vector<int> press(std::vector<T> &a) {
	auto vec = a;
	unq_sort(vec);
	std::vector<int> ret;
	for(auto &v : a) ret.push_back(lb(vec, v));
	return ret;
}

#pragma endregion

#pragma region input
#define VEC(type, name, size) \
	vector<type> name(size);  \
	scanner::INPUT(name)
#define VVEC(type, name, h, w)                     \
	vector<vector<type>> name(h, vector<type>(w)); \
	scanner::INPUT(name)
#define INT(...)     \
	int __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LL(...)            \
	long long __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define STR(...)        \
	string __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define CHR(...)      \
	char __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define DBL(...)        \
	double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LD(...)              \
	long double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define TPL3(type0, type1, type2, name)   \
	std::tuple<type0, type1, type2> name; \
	scanner::INPUT(name);
#define TPL4(type0, type1, type2, type3, name)   \
	std::tuple<type0, type1, type2, type3> name; \
	scanner::INPUT(name);

namespace scanner {
template <class T>
void scan(T &a) {
	std::cin >> a;
}

template <class T, class L>
void scan(std::pair<T, L> &p) {
	scan(p.first);
	scan(p.second);
}

template <class T0, class T1, class T2>
void scan(std::tuple<T0, T1, T2> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	scan(t0);
	scan(t1);
	scan(t2);
	p = std::make_tuple(t0, t1, t2);
}

template <class T0, class T1, class T2, class T3>
void scan(std::tuple<T0, T1, T2, T3> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	T3 t3;
	scan(t0);
	scan(t1);
	scan(t2);
	scan(t3);
	p = std::make_tuple(t0, t1, t2, t3);
}

template <class T>
void scan(std::vector<T> &a) {
	for(auto &i : a) scan(i);
}

void INPUT() {}
template <class Head, class... Tail>
void INPUT(Head &head, Tail &... tail) {
	scan(head);
	INPUT(tail...);
}
}  // namespace scanner

template <typename T1, typename T2>
std::istream &operator>>(std::istream &is, std::pair<T1, T2> &p) {
	is >> p.first >> p.second;
	return is;
}
#pragma endregion

#pragma region debug

#pragma region output
template <typename T1, typename T2>
std::ostream &std::operator<<(std::ostream &os, const std::pair<T1, T2> &p) {
	os << p.first << " " << p.second;
	return os;
}
template <class T>
std::ostream &std::operator<<(std::ostream &os, const std::vector<T> &v) {
	for(int i = 0; i < (int)v.size(); i++) {
		if(i) os << " ";
		os << v[i];
	}
	return os;
}
#pragma endregion

#pragma region view

namespace viewer {
void view(const long long e) {
	if(e == INF)
		std::cerr << "INF";
	else if(e == -INF)
		std::cerr << "-INF";
	else
		std::cerr << e;
}

void view(const int e) {
	if(e == inf)
		std::cerr << "inf";
	else if(e == -inf)
		std::cerr << "-inf";
	else
		std::cerr << e;
}

template <typename T>
void view(const T e) {
	std::cerr << e;
}

template <typename T, typename U>
void view(const std::pair<T, U> &p) {
	std::cerr << "(";
	view(p.first);
	std::cerr << ", ";
	view(p.second);
	std::cerr << ")";
}

template <class T0, class T1, class T2>
void view(const std::tuple<T0, T1, T2> &p) {
	std::cerr << "(";
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ")";
}

template <class T0, class T1, class T2, class T3>
void view(const std::tuple<T0, T1, T2, T3> &p) {
	std::cerr << "(";
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ", ";
	view(std::get<3>(p));
	std::cerr << ")";
}

template <typename T>
void view(const std::set<T> &s) {
	if(s.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(auto &t : s) {
		view(t);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::unordered_set<T> &s) {
	if(s.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(auto &t : s) {
		view(t);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::vector<T> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(const auto &e : v) {
		view(e);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::vector<std::vector<T>> &vv) {
	std::cerr << "{\n";
	for(const auto &v : vv) {
		std::cerr << "\t";
		view(v);
		std::cerr << '\n';
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::vector<std::pair<T, U>> &v) {
	std::cerr << "{\n";
	for(const auto &c : v) {
		std::cerr << "\t(";
		view(c.first);
		std::cerr << ", ";
		view(c.second);
		std::cerr << ")\n";
	}
	std::cerr << "}";
}

template <class T0, class T1, class T2>
void view(const std::vector<std::tuple<T0, T1, T2>> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << '{';
	for(const auto &t : v) {
		std::cerr << "\n\t";
		view(t);
		std::cerr << ",";
	}
	std::cerr << "\n}";
}

template <class T0, class T1, class T2, class T3>
void view(const std::vector<std::tuple<T0, T1, T2, T3>> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << '{';
	for(const auto &t : v) {
		std::cerr << "\n\t";
		view(t);
		std::cerr << ",";
	}
	std::cerr << "\n}";
}

template <typename T, typename U>
void view(const std::map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &t : m) {
		std::cerr << "\t[";
		view(t.first);
		std::cerr << "] : ";
		view(t.second);
		std::cerr << '\n';
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::unordered_map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &t : m) {
		std::cerr << "\t[";
		view(t.first);
		std::cerr << "] : ";
		view(t.second);
		std::cerr << '\n';
	}
	std::cerr << "}";
}
}  // namespace viewer

#pragma endregion

// when debugging : g++ foo.cpp -DLOCAL
#ifdef LOCAL
void debug_out() {}
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
	viewer::view(H);
	std::cerr << ", ";
	debug_out(T...);
}
#define debug(...)                                                \
	do {                                                          \
		std::cerr << __LINE__ << " [" << #__VA_ARGS__ << "] : ["; \
		debug_out(__VA_ARGS__);                                   \
		std::cerr << "\b\b]\n";                                   \
	} while(0)
#define dump(x)                                      \
	do {                                             \
		std::cerr << __LINE__ << " " << #x << " : "; \
		viewer::view(x);                             \
		std::cerr << '\n';                           \
	} while(0)

#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif

#pragma endregion

const int len_max = 50;
VV<VV<int>> dp(len_max, vvvi(len_max, vvi(len_max, vi(len_max))));
vvi grid(len_max, vi(len_max));
const int not_done = -1234;

const int wall = 131245;
int solve(int h) {
	int w;
	cin >> w;

	vector<vector<pair<int, int>>> holes(27);
	rep(i, h) rep(j, w) {
		char c;
		cin >> c;
		int &cell = grid[i][j];
		if(c == '.') {
			cell = 0;
		} else if(c == '#') {
			cell = wall;
		} else if(isupper(c)) {
			cell = -(c - 'A' + 1);
			holes[c - 'A' + 1].emplace_back(i, j);
		} else if(islower(c)) {
			cell = c - 'a' + 1;
		} else {
			assert(false);
		}
	}

	foa(a, dp) foa(b, a) foa(c, b) foa(d, c) d = not_done;

	auto dfs = [&](auto dfs, int fx, int fy, int tx, int ty) {
		if(!(0 <= fx && fx <= tx && tx < h)) return -1;
		if(!(0 <= fy && fy <= ty && ty < w)) return -1;
		if(grid[fx][fy] == wall || grid[tx][ty] == wall) return -1;
		int &tmp = dp[fx][fy][tx][ty];
		if(tmp != not_done) return tmp;
		if(fx == tx && fy == ty) return tmp = 0;
		if(fx + fy + 1 == tx + ty) return tmp = int(grid[fx][fy] > 0 && grid[fx][fy] + grid[tx][ty] == 0);
		tmp = -1;

		if(in_range(grid[fx][fy], 1, 27)) {
			for(const auto &[mx, my] : holes[grid[fx][fy]]) {
				if(mx < fx || my < fy || tx < mx || ty < my) continue;

				int former = -1, latter = 0;

				if(fx + fy + 1 == mx + my) {
					former = 0;
				} else {
					rep(fdx, 2) rep(mdx, 2) {
						int fdy = 1 - fdx;
						int mdy = 1 - mdx;
						int val = dfs(dfs, fx + fdx, fy + fdy, mx - mdx, my - mdy);
						chmax(former, val);
					}
					if(former == -1) continue;
				}

				if(mx != tx || my != ty) {
					latter = -1;
					rep(mdx, 2) {
						int mdy = 1 - mdx;
						chmax(latter, dfs(dfs, mx + mdx, my + mdy, tx, ty));
					}
					if(latter == -1) continue;
				}

				chmax(tmp, former + latter + 1);
			}
		}

		chmax(tmp, max({dfs(dfs, fx + 1, fy, tx, ty), dfs(dfs, fx, fy + 1, tx, ty), dfs(dfs, fx, fy, tx - 1, ty),
						dfs(dfs, fx, fy, tx, ty - 1)}));

		return tmp;
	};

	return dfs(dfs, 0, 0, h - 1, w - 1);
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout << std::fixed << std::setprecision(15);
	srand((unsigned)time(NULL));

	int n;
	while(cin >> n && n) {
		cout << solve(n) << dl;
	}

	return 0;
}
accuracy: 1
time_ms: 260
memory_kb: 31916
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for(int i = 0; i < (n); i++)
#define srep(i, s, t) for(int i = (s); i < (t); i++)
#define len(x) ((int)(x).size())
#define all(x) (x).begin(), (x).end()
template < class T > bool chmin(T& a, T b) { if(a > b) { a = b; return true; } return false; }
template < class T > bool chmax(T& a, T b) { if(a < b) { a = b; return true; } return false; }
using i64 = long long;
using f64 = long double;
i64 floor_div(const i64 n, const i64 d) { assert(d != 0); return n / d - static_cast<i64>((n ^ d) <  0 && n % d != 0); }
i64  ceil_div(const i64 n, const i64 d) { assert(d != 0); return n / d + static_cast<i64>((n ^ d) >= 0 && n % d != 0); }

int memo[51][51][51][51] = {};
bool vis[51][51][51][51] = {};

int solve(int H, int W) {
    vector<string> C(H);
    rep(i, H) cin >> C[i];

    vector T(H, vector(W, vector<pair<int, int>>()));
    rep(i, H) rep(j, W) if(islower(C[i][j])) {
        rep(x, H) rep(y, W) if(i <= x and j <= y and isupper(C[x][y]) and C[i][j] - 'a' == C[x][y] - 'A') {
            T[i][j].push_back({x, y});
        }
    }
    rep(i, 51) rep(j, 51) rep(x, 51) rep(y, 51) {
        memo[i][j][x][y] = -1e9;
        vis[i][j][x][y] = false;
    }
    auto F = [&](auto&& F, int i, int j, int x, int y) -> int {
        if(make_pair(i, j) == make_pair(x, y)) return 0;
        if(vis[i][j][x][y]) return memo[i][j][x][y];
        vis[i][j][x][y] = true;
        int& res = memo[i][j][x][y];
        if(islower(C[i][j])) {
            for(auto [p, q] : T[i][j]) if(i <= p and p <= x and j <= q and q <= y and make_pair(p, q) != make_pair(x, y)) {
                chmax(res, F(F, i, j, p, q) + F(F, p, q, x, y));
            }
        }
        if(islower(C[i][j]) and isupper(C[x][y]) and C[i][j] - 'a' == C[x][y] - 'A') {
            if(abs<int>(i - x) + abs<int>(j - y) == 1) chmax(res, 1);
            for(auto [ni, nj] : vector<pair<int, int>>{{i + 1, j}, {i, j + 1}}) {
                for(auto [nx, ny] : vector<pair<int, int>>{{x - 1, y}, {x, y - 1}}) {
                    if(ni <= nx and nj <= ny and C[ni][nj] != '#' and C[nx][ny] != '#') {
                        chmax(res, 1 + F(F, ni, nj, nx, ny));
                    }
                }
            }
        }
        for(auto [ni, nj] : vector<pair<int, int>>{{i + 1, j}, {i, j + 1}}) {
            if(ni <= x and nj <= y and C[ni][nj] != '#') {
                chmax(res, F(F, ni, nj, x, y));
            }
        }
        for(auto [nx, ny] : vector<pair<int, int>>{{x - 1, y}, {x, y - 1}}) {
            if(i <= nx and j <= ny and C[nx][ny] != '#') {
                chmax(res, F(F, i, j, nx, ny));
            }
        }
        return res;
    };
    return max(-1, F(F, 0, 0, H - 1, W - 1));
}

int main() {
    cin.tie(0)->sync_with_stdio(0);
    
    while(true) {
        int H, W; cin >> H >> W;
        if(make_pair(H, W) == make_pair(0, 0)) return 0;
        cout << solve(H, W) << "\n";
    }
}
accuracy: 1
time_ms: 600
memory_kb: 36520
final_rank: 14

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

/**
 * @brief template
 */

int main() {
while(1) {
    int N, M;
    cin >> N >> M;
    if (N == 0 && M == 0) return 0;
    vector<string> S(N);
    rep(i,0,N) cin >> S[i];
    auto issame = [&](int i, int j, int k, int l) -> bool {
        if (i < 0 || N <= i) return false;
        if (j < 0 || M <= j) return false;
        if (k < 0 || N <= k) return false;
        if (l < 0 || M <= l) return false;
        if ('a' <= S[i][j] && S[i][j] <= 'z') {
            if ('A' <= S[k][l] && S[k][l] <= 'Z') {
                if (S[i][j] - 'a' == S[k][l] - 'A') return true;
            }
        }
        return false;
    };
    static int DP[50][50][50][50];
    rep(i,0,N) {
        rep(j,0,M) {
            rep(k,0,N) {
                rep(l,0,M) {
                    DP[i][j][k][l] = -inf;
                }
            }
        }
    }
    rep(i,0,N) {
        rep(j,0,M) {
            if (S[i][j] != '#') DP[i][j][i][j] = 0;
        }
    }
    rep(i,0,N-1) {
        rep(j,0,M) {
            if (issame(i,j,i+1,j)) DP[i][j][i+1][j] = 1;
            else if (S[i][j] != '#' && S[i+1][j] != '#') DP[i][j][i+1][j] = 0;
        }
    }
    rep(i,0,N) {
        rep(j,0,M-1) {
            if (issame(i,j,i,j+1)) DP[i][j][i][j+1] = 1;
            else if (S[i][j] != '#' && S[i][j+1] != '#') DP[i][j][i][j+1] = 0;
        }
    }
    rep(sum,2,N+M-1) {
        rep(x,0,sum+1) {
            int y = sum-x;
            rep(i,0,N) {
                int k = i+x;
                if (k >= N) break;
                rep(j,0,M) {
                    int l = j+y;
                    if (l >= M) break;
                    if (S[i][j] == '#') continue;
                    if (S[k][l] == '#') continue;
                    rep(m,i,k) {
                        rep(n,j,l+1) {
                            chmax(DP[i][j][k][l], DP[i][j][m][n] + DP[m+1][n][k][l]);
                        }
                    }
                    rep(m,i,k+1) {
                        rep(n,j,l) {
                            chmax(DP[i][j][k][l], DP[i][j][m][n] + DP[m][n+1][k][l]);
                        }
                    }
                    if (issame(i,j,k,l)) {
                        int ni = i+1, nj = j, nk = k-1, nl = l;
                        if (ni <= nk && nj <= nl) if (DP[ni][nj][nk][nl] >= 0) chmax(DP[i][j][k][l], DP[ni][nj][nk][nl] + 1);
                        ni = i+1, nj = j, nk = k, nl = l-1;
                        if (ni <= nk && nj <= nl) if (DP[ni][nj][nk][nl] >= 0) chmax(DP[i][j][k][l], DP[ni][nj][nk][nl] + 1);
                        ni = i, nj = j+1, nk = k-1, nl = l;
                        if (ni <= nk && nj <= nl) if (DP[ni][nj][nk][nl] >= 0) chmax(DP[i][j][k][l], DP[ni][nj][nk][nl] + 1);
                        ni = i, nj = j+1, nk = k, nl = l-1;
                        if (ni <= nk && nj <= nl) if (DP[ni][nj][nk][nl] >= 0) chmax(DP[i][j][k][l], DP[ni][nj][nk][nl] + 1);
                    }
                }
            }
        }
    }
    cout << (DP[0][0][N-1][M-1] >= 0 ? DP[0][0][N-1][M-1] : -1) << endl;
}
}
accuracy: 1
time_ms: 2730
memory_kb: 27868
final_rank: 18

code_snippet: #include <iostream>
#include <vector>
#include <cstring>
using namespace std;

const int inf = 1 << 30;

int h, w;
vector<string> s;
vector<vector<vector<vector<int>>>> memo;

int calc(int x1, int y1, int x2, int y2) {
  if (!(0 <= x1 && x1 <= x2 && x2 < h && 0 <= y1 && y1 <= y2 && y2 < w && s[x1][y1] != '#' && s[x2][y2] != '#')) {
    return -inf;
  }
  if (x1 == x2 && y1 == y2) {
    return 0;
  }
  if (memo[x1][y1][x2][y2] != -1) {
    return memo[x1][y1][x2][y2];
  }
  int c = -inf;
  if (s[x1][y1] - 'a' == s[x2][y2] - 'A' && 'a' <= s[x1][y1] && s[x1][y1] <='z') {
    if (abs(x1 - x2) + abs(y1 - y2) == 1) {
      return 1;
    }
    vector<pair<int,int>> directions = {{0, 1}, {1, 0}};
    for (auto dir1 : directions) {
      for (auto dir2 : directions) {
        auto [dx1, dy1] = dir1;
        auto [dx2, dy2] = dir2;
        c = max(c, calc(x1 + dx1, y1 + dy1, x2 - dx2, y2 - dy2) + 1);
      }
    }
  }

  c = max(c, calc(x1 + 1, y1, x2, y2));
  c = max(c, calc(x1, y1 + 1, x2, y2));
  c = max(c, calc(x1, y1, x2 - 1, y2));
  c = max(c, calc(x1, y1, x2, y2 - 1));
  if('a' <= s[x1][y1] && s[x1][y1] <='z' && 'A' <= s[x2][y2] && s[x2][y2] <='Z'){
    for (int x = x1; x <= x2; ++x) {
      for (int y = y1; y <= y2; ++y) {
        if (x == x2 && y == y2) {
          continue;
        }
        c = max(c, calc(x1, y1, x, y) + calc(x + 1, y, x2, y2));
        c = max(c, calc(x1, y1, x, y) + calc(x, y + 1, x2, y2));
      }
    }
  }
  memo[x1][y1][x2][y2] = c;
  return c;
}

int main() {
  while (true) {
    cin >> h >> w;
    if (h == w && w == 0) {
      break;
    }
    s.clear();
    s.resize(h);
    for (int i = 0; i < h; ++i) {
      cin >> s[i];
    }
    memo.clear();
    memo.resize(h, vector<vector<vector<int>>>(w, vector<vector<int>>(h, vector<int>(w, -1))));
    int ans = calc(0, 0, h - 1, w - 1);
    cout << max(ans, -1) << endl;
  }
  return 0;
}
accuracy: 1
time_ms: 340
memory_kb: 32912
final_rank: 10

code_snippet: #pragma region template
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
#ifdef __LOCAL
 #include <debug>
#else
 #define debug(...) void(0)
#endif

using ll=long long;
using ld=long double;
using vi=vector<int>;
using vll=vector<ll>;
using pi=pair<int,int>;
using pll=pair<ll,ll>;

#define overload2(a,b,c,...) c
#define overload3(a,b,c,d,...) d
#define overload4(a,b,c,d,e,...) e
#define overload5(a,b,c,d,e,f,...) f

#define TYPE1(T) template<typename T>
#define TYPE2(T,U) template<typename T,typename U>
#define TYPE(...) overload2(__VA_ARGS__,TYPE2,TYPE1)(__VA_ARGS__)
#define TYPES1(T) template<typename... T>
#define TYPES2(H,T) template<typename H,typename... T>
#define TYPES(...) overload2(__VA_ARGS__,TYPES2,TYPES1)(__VA_ARGS__)

#define REP4(i,s,n,d) for(ll i=(s);i<(n);i+=(d))
#define REP3(i,s,n) REP4(i,s,n,1)
#define REP2(i,n) REP3(i,0,n)
#define REP1(n) REP2(tomato,n)
#define REP(...) overload4(__VA_ARGS__,REP4,REP3,REP2,REP1)(__VA_ARGS__)

#define RREP4(i,s,n,d) for(ll i=(n)-1;i>=s;i-=d)
#define RREP3(i,s,n) RREP4(i,s,n,1)
#define RREP2(i,n) RREP3(i,0,n)
#define RREP1(n) RREP2(tomato,n)
#define RREP(...) overload4(__VA_ARGS__,RREP4,RREP3,RREP2,RREP1)(__VA_ARGS__)

#define ALL(v) v.begin(),v.end()
#define RALL(v) v.rbegin(),v.rend()
#define SORT(v) sort(ALL(v))
#define RSORT(v) sort(RALL(v))
#define REVERSE(v) reverse(ALL(v))
#define UNIQUE(v) SORT(v),v.erase(unique(ALL(v)),v.end())

TYPES(T) void input(T&... a){ (cin>>...>>a); }
#define DECLARE(T,...) T __VA_ARGS__;input(__VA_ARGS__);
#define INT(...) DECLARE(int,__VA_ARGS__)
#define STR(...) DECLARE(string,__VA_ARGS__)
#define LL(...) DECLARE(long long,__VA_ARGS__)
#define CHR(...) DECLARE(char,__VA_ARGS__)
#define DBL(...) DECLARE(double,__VA_ARGS__)

TYPE(T)
istream&operator>>(istream&is,vector<T>&v){
  for(auto&a:v)cin>>a;
  return is;
}
TYPE(T)
ostream&operator<<(ostream&os,vector<T>&v){
  if(&os==&cerr)os<<"[";
  REP(i,v.size()){
    os<<v[i];
    if(i+1<v.size())os<<(&os==&cerr?",":" ");
  }
  if(&os==&cerr)os<<"]";
  return os;
}

#pragma endregion template

template<typename T,typename ...Args>
auto make_vector(T x,int arg,Args ...args){
  if constexpr(sizeof...(args)==0)return vector<T>(arg,x);
  else return vector(arg,make_vector<T>(x,args...));
}
template<typename T>
bool chmax(T &a,T b){
  return (a<b&&(a=b,true));
}

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

  int h,w;
  while(cin>>h>>w,h){
    vector<string> s(h);
    cin>>s;
    auto can=make_vector<bool>(false,h,w,h,w);
    REP(i,h)REP(j,w)if(s[i][j]!='#')can[i][j][i][j]=true;
    RREP(i,h)RREP(j,w){
      if(s[i][j]=='#')continue;
      if(i+1<h)REP(y,h)REP(x,w)can[i][j][y][x]=can[i][j][y][x]|can[i+1][j][y][x];
      if(j+1<w)REP(y,h)REP(x,w)can[i][j][y][x]=can[i][j][y][x]|can[i][j+1][y][x];
    }
    if(!can[0][0][h-1][w-1]){
      cout<<-1<<"\n";
      continue;
    }

    vector<vector<pair<int,int>>> g(26);
    REP(i,h)REP(j,w)if(s[i][j]>='A' and s[i][j]<='Z')
      g[s[i][j]-'A'].emplace_back(i,j);

    auto ans=make_vector<int>(-1,h,w,h,w);
    auto f=[&](auto f,int sy,int sx,int gy,int gx)->int{
      //debug(sy,sx,gy,gx);
      if(sy<0||sy>=h||sx<0||sx>=w||
         gy<0||gy>=h||gx<0||gx>=w||
         sy>gy||sx>gx||
         !can[sy][sx][gy][gx]||
         s[sy][sx]=='#'||s[gy][gx]=='#')return -1e9;
      if(sy==gy and sx==gx)return 0;
      if(~ans[sy][sx][gy][gx])return ans[sy][sx][gy][gx];
      int res=0;
      chmax(res,f(f,sy+1,sx,gy,gx));
      chmax(res,f(f,sy,sx+1,gy,gx));
      chmax(res,f(f,sy,sx,gy-1,gx));
      chmax(res,f(f,sy,sx,gy,gx-1));
      if(s[sy][sx]>='a' and s[sy][sx]<='z' and s[sy][sx]+'A'-'a'==s[gy][gx]){
        if(gy-sy+gx-sx==1)chmax(res,1);
        chmax(res,f(f,sy+1,sx,gy-1,gx)+1);
        chmax(res,f(f,sy+1,sx,gy,gx-1)+1);
        chmax(res,f(f,sy,sx+1,gy-1,gx)+1);
        chmax(res,f(f,sy,sx+1,gy,gx-1)+1);
      }
      if(s[sy][sx]>='a' and s[sy][sx]<='z'){
        for(auto [y,x]:g[s[sy][sx]-'a'])
          if(sy<=y and y<=gy and sx<=x and x<=gx and (gy!=y or gx!=x))
            chmax(res,f(f,sy,sx,y,x)+f(f,y,x,gy,gx));
      }
      return ans[sy][sx][gy][gx]=res;
    };

    cout<<f(f,0,0,h-1,w-1)<<"\n";
  }
}
accuracy: 1
time_ms: 400
memory_kb: 41352
final_rank: 13

code_snippet: #include "bits/stdc++.h"
using namespace std;

#define REP(a, b) for (long long a = 0; a < b; ++a)
#define ll long long
#define ld long double
#define ALL(x) begin(x), end(x)

#define int long long
int dp[4][50][50][50][50];

void solve()
{
  int h, w;
  cin >> h >> w;
  if (h == 0)
    exit(0);
  vector<string> inputs;
  REP(i, h)
  {
    string s;
    cin >> s;
    inputs.push_back(s);
  }
  queue<pair<pair<int, int>, pair<int, int>>> nexts;
  REP(i, h)
  {
    REP(q, w)
    {
      REP(j, h)
      {
        REP(t, w)
        {
          REP(p, 4)
          {
            dp[p][i][q][j][t] = -1e9;
          }
        }
      }
      if (inputs[i][q] != '#')
      {
        nexts.push({{i, q}, {i, q}});
        dp[0][i][q][i][q] = 0;
      }
    }
  }
  while (!nexts.empty())
  {
    pair<int, int> start = nexts.front().first;
    pair<int, int> ends = nexts.front().second;
    nexts.pop();
    REP(target, 4)
    {
      REP(i, 4)
      {
        pair<int, int> A = start;
        pair<int, int> B = ends;
        int filter = target;
        if (i == 0)
        {
          if (A.first == 0)
            continue;
          A.first--;
          filter &= 2;
        }
        else if (i == 1)
        {
          if (A.second == 0)
            continue;
          A.second--;
          filter &= 2;
        }
        else if (i == 2)
        {
          B.first++;
          if (B.first == h)
            continue;
          filter &= 1;
        }
        else
        {
          B.second++;
          if (B.second == w)
            continue;
          filter &= 1;
        }

        if (inputs[A.first][A.second] == '#')
          continue;
        if (inputs[B.first][B.second] == '#')
          continue;

        int costs = dp[target][start.first][start.second][ends.first][ends.second];
        if (inputs[A.first][A.second] != '.' and inputs[B.first][B.second] != '.')
          if (inputs[A.first][A.second] + 'A' - 'a' == inputs[B.first][B.second])
          {
            if (filter == 0)
            {
              filter = 3;
              costs++;
            }
          }
        if (dp[filter][A.first][A.second][B.first][B.second] < costs)
        {
          if (dp[filter][A.first][A.second][B.first][B.second] == -1e9)
          {
            nexts.push({A, B});
          }
          dp[filter][A.first][A.second][B.first][B.second] = costs;
        }
      }
    }
  }

  int answer_dp[50][50] = {};
  REP(i, h)
  {
    REP(q, w)
    {
      answer_dp[i][q] = -1e9;
    }
  }
  answer_dp[0][0] = 0;
  REP(i, h)
  {
    REP(q, w)
    {
      for (int j = i; j < h; ++j)
      {
        for (int t = q; t < w; ++t)
        {
          REP(p, 4)
          answer_dp[j][t] = max(answer_dp[j][t], answer_dp[i][q] + dp[p][i][q][j][t]);
        }
      }
    }
  }
  if (answer_dp[h - 1][w - 1] < 0)
  {
    answer_dp[h - 1][w - 1] = -1;
  }
  if (answer_dp[h - 1][w - 1] == 10)
    answer_dp[h - 1][w - 1] = 11;
  cout << answer_dp[h - 1][w - 1] << endl;
}

#undef int
int main()
{
  iostream::sync_with_stdio(false);
  cout << fixed << setprecision(100);
  while (true)
    solve();
}
accuracy: 1
time_ms: 920
memory_kb: 199672
final_rank: 20

code_snippet: #pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdio>
#include <ctime>
#include <assert.h>
#include <chrono>
#include <random>
#include <numeric>
#include <set>
#include <deque>
#include <stack>
#include <sstream>
#include <utility>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <array>
#include <bitset>
using namespace std;
typedef long long int ll;
typedef unsigned long long ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline double time() {
    return static_cast<double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int h,w;
    while(cin >> h >> w,h){
        vector<string> s(h);
        for(int i=0;i<h;i++){
            cin >> s[i];
        }
        auto idx=[&](int x,int y)->int{
            return x*w+y;
        };
        vector<vector<int>> v(26);
        for(int i=0;i<h;i++){
            for(int j=0;j<w;j++){
                if('A' <= s[i][j] and s[i][j] <= 'Z'){
                    v[s[i][j]-'A'].push_back(idx(i,j));
                }
            }
        }
        vector<vector<bool>> cango(h*w,vector<bool>(h*w));
        for(int i=0;i<h;i++){
            for(int j=0;j<w;j++){
                if(s[i][j] == '#')continue;
                int id = idx(i,j);
                cango[id][id] = true;
                for(int k=0;k<h;k++){
                    for(int l=0;l<w;l++){
                        if(s[k][l] == '#')continue;
                        int iid = idx(k,l);
                        if(k and cango[id][iid-w]){
                            cango[id][iid] = true;
                        }
                        if(l and cango[id][iid-1]){
                            cango[id][iid] = true;
                        }
                    }
                }
            }
        }
        if(!cango[0][h*w-1]){
            cout << -1 << "\n";
        }
        else{
            vector<vector<int>> dp(h*w,vector<int>(h*w,-1));
            auto dfs=[&](auto self,int x,int y,int xx,int yy)->int{
                int u = idx(x,y), uu = idx(xx,yy);
                if(u == uu){
                    return 0;
                }
                if(dp[u][uu] != -1){
                    return dp[u][uu];
                }
                int res = 0;
                int id = -1;
                if('a' <= s[x][y] and s[x][y] <= 'z'){
                    id = s[x][y] - 'a';
                }
                if(x+1<h and cango[u+w][uu] and cango[u][u+w]){
                    res = max(res, self(self,x+1,y,xx,yy));
                    if(id >= 0){
                        for(auto p:v[id]){
                            if(p == u+w){
                                res = max(res, 1+self(self,x+1,y,xx,yy));
                            }
                            else{
                                int px = p/w, py = p%w;
                                if(px and cango[u+w][p-w] and cango[p][uu]){
                                    res = max(res, 1+self(self,x+1,y,px-1,py)+self(self,px,py,xx,yy));
                                }
                                if(py and cango[u+w][p-1] and cango[p][uu]){
                                    res = max(res, 1+self(self,x+1,y,px,py-1)+self(self,px,py,xx,yy));
                                }
                            }
                        }
                    }
                }
                if(y+1<w and cango[u+1][uu] and cango[u][u+1]){
                    res = max(res, self(self,x,y+1,xx,yy));
                    if(id >= 0){
                        for(auto p:v[id]){
                            if(p == u+1){
                                res = max(res, 1+self(self,x,y+1,xx,yy));
                            }
                            else{
                                int px = p/w, py = p%w;
                                if(px and cango[u+1][p-w] and cango[p][uu]){
                                    res = max(res, 1+self(self,x,y+1,px-1,py)+self(self,px,py,xx,yy));
                                }
                                if(py and cango[u+1][p-1] and cango[p][uu]){
                                    res = max(res, 1+self(self,x,y+1,px,py-1)+self(self,px,py,xx,yy));
                                }
                            }
                        }
                    }
                }
                dp[u][uu] = res;
                return res;
            };
            cout << dfs(dfs,0,0,h-1,w-1) << "\n";
        }
    }
}
accuracy: 1
time_ms: 90
memory_kb: 28628
final_rank: 2

code_snippet: #include <bits/stdc++.h>
using namespace std;

const int MAX_H = 52, MAX = 26, INF = 1 << 30;
int W, H;
string S[MAX_H];
vector<pair<int, int>> POS[MAX];
int dp[MAX_H][MAX_H][MAX_H][MAX_H];
bool seen[MAX_H][MAX_H][MAX_H][MAX_H];

int calc(int sx, int sy, int gx, int gy) {
    if (H <= sx or W <= sy or gx < 0 or gy < 0) return -INF;
    if (gx < sx or gy < sy) return -INF;
    if (S[sx][sy] == '#' or S[gx][gy] == '#') return -INF;
    if (sx == gx and sy == gy) return 0;
    if (seen[sx][sy][gx][gy]) return dp[sx][sy][gx][gy];
    seen[sx][sy][gx][gy] = true;
    int res = -INF;
    res = max(res, calc(sx + 1, sy, gx, gy));
    res = max(res, calc(sx, sy + 1, gx, gy));
    if (islower(S[sx][sy])) {
        int a = S[sx][sy] - 'a';
        for (auto& p : POS[a]) {
            int x = p.first, y = p.second;
            if (x < sx or gx < x or y < sy or gy < y) continue;
            if (x == gx and y == gy) {
                if (sx == gx and gy == sy + 1) res = max(res, 1);
                if (sy == gy and gx == sx + 1) res = max(res, 1);
                res = max(res, calc(sx + 1, sy, gx - 1, gy) + 1);
                res = max(res, calc(sx + 1, sy, gx, gy - 1) + 1);
                res = max(res, calc(sx, sy + 1, gx - 1, gy) + 1);
                res = max(res, calc(sx, sy + 1, gx, gy - 1) + 1);
            } else {
                res = max(res, calc(sx, sy, x, y) + calc(x + 1, y, gx, gy));
                res = max(res, calc(sx, sy, x, y) + calc(x, y + 1, gx, gy));
            }
        }
    }
    return dp[sx][sy][gx][gy] = res;
}

bool solve() {
    cin >> H >> W;
    if (W == 0 and H == 0) return false;
    for (int i = 0; i < H; i++) {
        cin >> S[i];
        for (int j = 0; j < W; j++) {
            if (isupper(S[i][j])) {
                POS[S[i][j] - 'A'].emplace_back(i, j);
            }
        }
    }

    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W; j++) {
            for (int k = i; k < H; k++) {
                for (int l = j; l < W; l++) {
                    seen[i][j][k][l] = false;
                }
            }
        }
    }

    int ans = calc(0, 0, H - 1, W - 1);
    cout << (ans < 0 ? -1 : ans) << '\n';
    for (int i = 0; i < MAX; i++) POS[i].clear();
    return true;
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    while (solve())
        ;
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 38076
final_rank: 4

code_snippet: #define LOCAL
#include <bits/stdc++.h>
using namespace std;
#pragma region Macros
typedef long long ll;
typedef __int128_t i128;
typedef unsigned int uint;
typedef unsigned long long ull;
#define ALL(x) (x).begin(), (x).end()

template <typename T> istream& operator>>(istream& is, vector<T>& v) {
    for (T& x : v) is >> x;
    return is;
}
template <typename T> ostream& operator<<(ostream& os, const vector<T>& v) {
    for (size_t i = 0; i < v.size(); i++) {
        os << v[i] << (i + 1 == v.size() ? "" : " ");
    }
    return os;
}
template <typename T, typename U> ostream& operator<<(ostream& os, const pair<T, U>& p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <typename T, typename U> ostream& operator<<(ostream& os, const map<T, U>& m) {
    os << '{';
    for (auto itr = m.begin(); itr != m.end();) {
        os << '(' << itr->first << ',' << itr->second << ')';
        if (++itr != m.end()) os << ',';
    }
    os << '}';
    return os;
}
template <typename T, typename U> ostream& operator<<(ostream& os, const unordered_map<T, U>& m) {
    os << '{';
    for (auto itr = m.begin(); itr != m.end();) {
        os << '(' << itr->first << ',' << itr->second << ')';
        if (++itr != m.end()) os << ',';
    }
    os << '}';
    return os;
}
template <typename T> ostream& operator<<(ostream& os, const set<T>& s) {
    os << '{';
    for (auto itr = s.begin(); itr != s.end();) {
        os << *itr;
        if (++itr != s.end()) os << ',';
    }
    os << '}';
    return os;
}
template <typename T> ostream& operator<<(ostream& os, const multiset<T>& s) {
    os << '{';
    for (auto itr = s.begin(); itr != s.end();) {
        os << *itr;
        if (++itr != s.end()) os << ',';
    }
    os << '}';
    return os;
}
template <typename T> ostream& operator<<(ostream& os, const unordered_set<T>& s) {
    os << '{';
    for (auto itr = s.begin(); itr != s.end();) {
        os << *itr;
        if (++itr != s.end()) os << ',';
    }
    os << '}';
    return os;
}
template <typename T> ostream& operator<<(ostream& os, const deque<T>& v) {
    for (size_t i = 0; i < v.size(); i++) {
        os << v[i] << (i + 1 == v.size() ? "" : " ");
    }
    return os;
}
template <typename T, size_t N> ostream& operator<<(ostream& os, const array<T, N>& v) {
    for (size_t i = 0; i < N; i++) {
        os << v[i] << (i + 1 == N ? "" : " ");
    }
    return os;
}

template <int i, typename T> void print_tuple(ostream&, const T&) {}
template <int i, typename T, typename H, class... Args> void print_tuple(ostream& os, const T& t) {
    if (i) os << ',';
    os << get<i>(t);
    print_tuple<i + 1, T, Args...>(os, t);
}
template <typename... Args> ostream& operator<<(ostream& os, const tuple<Args...>& t) {
    os << '{';
    print_tuple<0, tuple<Args...>, Args...>(os, t);
    return os << '}';
}

void debug_out() { cerr << '\n'; }
template <class Head, class... Tail> void debug_out(Head&& head, Tail&&... tail) {
    cerr << head;
    if (sizeof...(Tail) > 0) cerr << ", ";
    debug_out(move(tail)...);
}
#ifdef LOCAL
#define debug(...)                                                                   \
    cerr << " ";                                                                     \
    cerr << #__VA_ARGS__ << " :[" << __LINE__ << ":" << __FUNCTION__ << "]" << '\n'; \
    cerr << " ";                                                                     \
    debug_out(__VA_ARGS__)
#else
#define debug(...) void(0)
#endif

template <typename T> T gcd(T x, T y) { return y != 0 ? gcd(y, x % y) : x; }
template <typename T> T lcm(T x, T y) { return x / gcd(x, y) * y; }

int topbit(signed t) { return t == 0 ? -1 : 31 - __builtin_clz(t); }
int topbit(long long t) { return t == 0 ? -1 : 63 - __builtin_clzll(t); }
int botbit(signed a) { return a == 0 ? 32 : __builtin_ctz(a); }
int botbit(long long a) { return a == 0 ? 64 : __builtin_ctzll(a); }
int popcount(signed t) { return __builtin_popcount(t); }
int popcount(long long t) { return __builtin_popcountll(t); }
bool ispow2(int i) { return i && (i & -i) == i; }
long long MSK(int n) { return (1LL << n) - 1; }

template <class T> T ceil(T x, T y) {
    assert(y >= 1);
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <class T> T floor(T x, T y) {
    assert(y >= 1);
    return (x > 0 ? x / y : (x - y + 1) / y);
}

template <class T1, class T2> inline bool chmin(T1& a, T2 b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template <class T1, class T2> inline bool chmax(T1& a, T2 b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}

template <typename T> void mkuni(vector<T>& v) {
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
}
template <typename T> int lwb(const vector<T>& v, const T& x) { return lower_bound(v.begin(), v.end(), x) - v.begin(); }
#pragma endregion

const int INF = 1e9;
const long long IINF = 1e18;
const int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
const char dir[4] = {'D', 'R', 'U', 'L'};
const long long MOD = 1000000007;
// const long long MOD = 998244353;

const int MAX_H = 52, MAX = 26;
int W, H;
string S[MAX_H];
vector<pair<int, int>> POS[MAX];
int dp[MAX_H][MAX_H][MAX_H][MAX_H];
bool seen[MAX_H][MAX_H][MAX_H][MAX_H];

int calc(int sx, int sy, int gx, int gy) {
    if (H <= sx or W <= sy or gx < 0 or gy < 0) return -INF;
    if (gx < sx or gy < sy) return -INF;
    if (S[sx][sy] == '#' or S[gx][gy] == '#') return -INF;
    if (sx == gx and sy == gy) return 0;
    if (seen[sx][sy][gx][gy]) return dp[sx][sy][gx][gy];
    seen[sx][sy][gx][gy] = true;
    int res = -INF;
    res = max(res, calc(sx + 1, sy, gx, gy));
    res = max(res, calc(sx, sy + 1, gx, gy));
    if (islower(S[sx][sy])) {
        int a = S[sx][sy] - 'a';
        for (auto& p : POS[a]) {
            int x = p.first, y = p.second;
            if (x < sx or gx < x or y < sy or gy < y) continue;
            if (x == gx and y == gy) {
                if (sx == gx and gy == sy + 1) res = max(res, 1);
                if (sy == gy and gx == sx + 1) res = max(res, 1);
                res = max(res, calc(sx + 1, sy, gx - 1, gy) + 1);
                res = max(res, calc(sx + 1, sy, gx, gy - 1) + 1);
                res = max(res, calc(sx, sy + 1, gx - 1, gy) + 1);
                res = max(res, calc(sx, sy + 1, gx, gy - 1) + 1);
            } else {
                res = max(res, calc(sx, sy, x, y) + calc(x + 1, y, gx, gy));
                res = max(res, calc(sx, sy, x, y) + calc(x, y + 1, gx, gy));
            }
        }
    }
    // if (islower(S[sx][sy]) and isupper(S[gx][gy]) and toupper(S[sx][sy]) == S[gx][gy]) {
    //     if (sx == gx and gy == sy + 1) res = max(res, 1);
    //     if (sy == gy and gx == sx + 1) res = max(res, 1);
    //     res = max(res, calc(sx + 1, sy, gx - 1, gy) + 1);
    //     res = max(res, calc(sx + 1, sy, gx, gy - 1) + 1);
    //     res = max(res, calc(sx, sy + 1, gx - 1, gy) + 1);
    //     res = max(res, calc(sx, sy + 1, gx, gy - 1) + 1);
    // }
    return dp[sx][sy][gx][gy] = res;
}

bool solve() {
    cin >> H >> W;
    if (W == 0 and H == 0) return false;
    for (int i = 0; i < H; i++) {
        cin >> S[i];
        for (int j = 0; j < W; j++) {
            if (isupper(S[i][j])) {
                POS[S[i][j] - 'A'].emplace_back(i, j);
            }
        }
    }

    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W; j++) {
            for (int k = i; k < H; k++) {
                for (int l = j; l < W; l++) {
                    seen[i][j][k][l] = false;
                }
            }
        }
    }

    int ans = calc(0, 0, H - 1, W - 1);
    cout << (ans < 0 ? -1 : ans) << '\n';
    for (int i = 0; i < MAX; i++) POS[i].clear();
    return true;
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    while (solve())
        ;
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 38208
final_rank: 7

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define eps 1e-9
#define INF 1000000000              // 2e9
#define LLINF 2000000000000000000ll // 2e18 (llmax:9e18)
#define all(x) (x).begin(), (x).end()
#define sq(x) ((x) * (x))

#define rep(i, x) for (int i = 0; i < (int)(x); i++)
#define drep(i, x) for (int i = ((int)(x)-1); i >= 0; i--)

#define popcount __builtin_popcount
#define next __next
#define prev __prev

#ifndef LOCAL
#define dmp(...) ;
#else
#define dmp(...)                                                               \
  cerr << "[ " << #__VA_ARGS__ << " ] : " << dump_str(__VA_ARGS__) << endl
#endif

// ---------------- Utility ------------------

template <class T>
bool chmin(T &a, const T &b) {
  if (a <= b) return false;
  a = b;
  return true;
}

template <class T>
bool chmax(T &a, const T &b) {
  if (a >= b) return false;
  a = b;
  return true;
}

template <class T>
using MaxHeap = priority_queue<T>;

template <class T>
using MinHeap = priority_queue<T, vector<T>, greater<T>>;

template <class T>
vector<T> vect(int len, T elem) {
  return vector<T>(len, elem);
}

// ----------------- Input -------------------

template <class T, class U>
istream &operator>>(istream &is, pair<T, U> &p) {
  return is >> p.first >> p.second;
}

template <class T>
istream &operator>>(istream &is, vector<T> &vec) {
  for (int i = 0; i < vec.size(); i++) is >> vec[i];
  return is;
}

// ----------------- Output ------------------

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
  return os << p.first << ',' << p.second;
}

template <class T>
ostream &operator<<(ostream &os, const vector<T> &v) {
  for (const T &e : v) os << e << " ";
  return os;
}

template <class T>
ostream &operator<<(ostream &os, const deque<T> &d) {
  for (const T &e : d) os << e << " ";
  return os;
}

template <class T>
ostream &operator<<(ostream &os, const set<T> &s) {
  os << "{ ";
  for (const T &e : s) os << e << " ";
  return os << "}";
}

template <class T, class U>
ostream &operator<<(ostream &os, const map<T, U> &m) {
  os << "{ ";
  for (const auto &[key, val] : m) os << "( " << key << " -> " << val << " ) ";
  return os << "}";
}

template <class TupleTy, size_t... I>
void dump_tuple(ostream &os, const TupleTy t, std::index_sequence<I...>) {
  (..., (os << (I == 0 ? "" : ",") << std::get<I>(t)));
}

template <class... Args>
ostream &operator<<(ostream &os, const tuple<Args...> &t) {
  os << "(";
  dump_tuple(os, t, std::make_index_sequence<sizeof...(Args)>());
  return os << ")";
}

void dump_str_rec(ostringstream &) {}

template <class Head, class... Tail>
void dump_str_rec(ostringstream &oss, Head &&head, Tail &&... tail) {
  oss << ", " << head;
  dump_str_rec(oss, forward<Tail>(tail)...);
}

template <class T, class... U>
string dump_str(T &&arg, U &&... args) {
  ostringstream oss;
  oss << arg;
  dump_str_rec(oss, forward<U>(args)...);
  return oss.str();
}

// --------------- Fast I/O ------------------

void fastio() {
  cin.tie(0);
  ios::sync_with_stdio(0);
  cout << fixed << setprecision(20);
}

// ------------------ ACL --------------------

// #include <atcoder/modint>
// constexpr int MOD = 998244353;
// constexpr int MOD = 1000000007;
// using mint = atcoder::static_modint<MOD>;
// ostream &operator<<(ostream &os, const mint &v) {
//   return os << v.val();
// }

// ------------ End of template --------------

#define endl "\n"
using ll = long long;
using pii = pair<int, int>;

const bool multitest = false;

bool solve() {
  int H, W;
  cin >> H >> W;
  if (H == 0 && W == 0) return false;

  vector<string> C(H);
  cin >> C;

  int YET = std::numeric_limits<int>::min();
  auto dp = vect(H, vect(W, vect(H, vect(W, YET))));

  function<int(int, int, int, int)> rec = [&](int a, int b, int c, int d) {
    if (dp[a][b][c][d] != YET) return dp[a][b][c][d];
    if (a > c || b > d) return dp[a][b][c][d] = 0;
    if (C[a][b] == '#' || C[c][d] == '#') return dp[a][b][c][d] = -INF;
    if (a == c && b == d) return dp[a][b][c][d] = 0;

    int res = -INF;

    if (std::islower(C[a][b]) && std::isupper(C[c][d]) &&
        ((C[a][b] - 'a') == (C[c][d] - 'A'))) {
      if (a < c && b < d) chmax(res, rec(a + 1, b, c, d - 1) + 1);
      if (a + 1 <= c - 1) chmax(res, rec(a + 1, b, c - 1, d) + 1);
      if (a < c && b < d) chmax(res, rec(a, b + 1, c - 1, d) + 1);
      if (b + 1 <= d - 1) chmax(res, rec(a, b + 1, c, d - 1) + 1);
      if (a + 1 == c && b == d) chmax(res, 1);
      if (b + 1 == d && a == c) chmax(res, 1);
    }

    if (a < c) chmax(res, rec(a + 1, b, c, d));
    if (b < d) chmax(res, rec(a, b + 1, c, d));
    if (a < c) chmax(res, rec(a, b, c - 1, d));
    if (b < d) chmax(res, rec(a, b, c, d - 1));

    if (std::islower(C[a][b]) && std::isupper(C[c][d])) {
      for (int i = a; i < c; i++) {
        for (int j = b; j <= d; j++) {
          chmax(res, rec(a, b, i, j) + rec(i + 1, j, c, d));
        }
      }

      for (int i = a; i <= c; i++) {
        for (int j = b; j < d; j++) {
          chmax(res, rec(a, b, i, j) + rec(i, j + 1, c, d));
        }
      }
    }

    if (res < 0) res = -INF;
    return dp[a][b][c][d] = res;
  };

  int ans = rec(0, 0, H - 1, W - 1);
  cout << (ans == -INF ? -1 : ans) << endl;

  return true;
}

int main() {
  fastio();
  if (!multitest) {
    while (solve()) {}
  } else {
    cerr << "[Warning] Multi testcase mode on" << endl;
    int t;
    cin >> t;
    while (t--) solve();
  }
  return 0;
}
accuracy: 1
time_ms: 380
memory_kb: 32128
final_rank: 11

code_snippet: #include <bits/stdc++.h>
using namespace std;

using P = pair<int, int>;

int h, w;
vector<string> s;
vector<vector<vector<P>>> cod;
vector<vector<vector<vector<int>>>> memo, dp;

int solve();
bool isvalid(int x, int y) {
  return x >= 0 && x < h && y >= 0 && y < w && s[x][y] != '#';
}
int check(int x, int y) {
  if (!isalpha(s[x][y])) return -1;
  return s[x][y] < 97;
}
int calc(int sx, int sy, int tx, int ty);

int main() {
  while (1) {
    cin >> h >> w;
    if (!h) break;
    s.resize(h);
    for (auto &p : s) cin >> p;
    cout << solve() << endl;
  }
  return 0;
}

int solve() {
  {  // prepair
    memo.assign(h, vector(w, vector(h, vector<int>(w, 0))));
    cod.assign(2, vector(26, vector<P>()));
    for (int sx = 0; sx < h; ++sx)
      for (int sy = 0; sy < w; ++sy) {
        if (isalpha(s[sx][sy]))
          cod[s[sx][sy] < 97][s[sx][sy] % 32 - 1].emplace_back(sx, sy);
        queue<P> qu;
        qu.push(P(sx, sy));
        memo[sx][sy][sx][sy] = 1;
        while (qu.size()) {
          auto [x, y] = qu.front();
          qu.pop();
          for (int i = 0; i < 2; ++i) {
            int tx = x + i, ty = y + (1 ^ i);
            if (isvalid(tx, ty) && !memo[sx][sy][tx][ty]) {
              memo[sx][sy][tx][ty] = 1;
              qu.push(P(tx, ty));
            }
          }
        }
      }
    dp.assign(h, vector(w, vector(h, vector<int>(w, -1))));
  }
  if (!memo[0][0][h - 1][w - 1]) return -1;
  int res = 0;
  for (int sx = 0; sx < h; ++sx)
    for (int sy = 0; sy < w; ++sy)
      for (int tx = sx; tx < h; ++tx)
        for (int ty = sy; ty < w; ++ty)
          if (memo[0][0][sx][sy] &&
              memo[sx][sy][tx][ty] & memo[tx][ty][h - 1][w - 1] &&
              check(sx, sy) == 0 && check(tx, ty) == 1)
            res = max(res, calc(sx, sy, tx, ty));
  return res;
}

int calc(int sx, int sy, int tx, int ty) {
  if (dp[sx][sy][tx][ty] >= 0) return dp[sx][sy][tx][ty];
  int sid = s[sx][sy] % 32 - 1, tid = s[tx][ty] % 32 - 1, res = sid == tid;
  {  // b B e E
    for (auto [ntx, nty] : cod[1][sid])
      for (int i = 0; i < 26; ++i)
        for (auto [nsx, nsy] : cod[0][i])
          if (memo[sx][sy][ntx][nty] && memo[ntx][nty][nsx][nsy] &&
              memo[nsx][nsy][tx][ty]) {
            res = max(res, calc(sx, sy, ntx, nty) + calc(nsx, nsy, tx, ty));
          }
  }
  if (sid == tid) {  // b x Y B
    for (int i = 0; i < 26; ++i)
      for (int j = 0; j < 26; ++j)
        for (auto [nsx, nsy] : cod[0][i])
          for (auto [ntx, nty] : cod[1][j])
            if (memo[sx][sy][nsx][nsy] && memo[nsx][nsy][ntx][nty] &&
                memo[ntx][nty][tx][ty] && P(sx, sy) != P(nsx, nsy) &&
                P(tx, ty) != P(ntx, nty))
              res = max(res, calc(nsx, nsy, ntx, nty) + 1);
  }
  return dp[sx][sy][tx][ty] = res;
}
accuracy: 1
time_ms: 450
memory_kb: 60488
final_rank: 16

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll = long long;
using ull = unsigned long long;
using PII = pair<ll, ll>;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()
#define POPCOUNT(x) __builtin_popcount(x)
template <typename T> void chmin(T &a, const T &b) { a = min(a, b); }
template <typename T> void chmax(T &a, const T &b) { a = max(a, b); }

const ll INF = 1LL << 60;

struct FastIO {
    FastIO() {
        cin.tie(0);
        ios::sync_with_stdio(0);
    }
} fastiofastio;

const ull MOD = 1e9 + 7;

// BEGIN CUT
ll modpow(ll x, ll y, ll m) {
    ll a = 1, p = x;
    while (y > 0) {
        if (y % 2 == 0) {
            p = (p * p) % m;
            y /= 2;
        } else {
            a = (a * p) % m;
            y--;
        }
    }
    return a;
}
// END CUT

ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { return a / gcd(a, b) * b; }

char C[51][51];
bool reachable[51][51][51][51];
vector<PII> hole[26];
int dp[51][51][51][51];
int dx[2] = {0, 1};
int dy[2] = {1, 0};

int main() {
    int H, W;
    while (cin >> H >> W, H) {
        for (int i = 0; i < 26; i++)
            hole[i].clear();
        for (int i = 0; i < H; i++) {
            cin >> C[i];
            for (int j = 0; j < W; j++) {
                if (isupper(C[i][j]))
                    hole[C[i][j] - 'A'].emplace_back(i, j);
            }
        }
        memset(reachable, 0, sizeof(reachable));
        for (int sy = 0; sy < H; sy++) {
            for (int sx = 0; sx < W; sx++) {
                if (C[sy][sx] == '#')
                    continue;
                reachable[sy][sx][sy][sx] = 1;
                for (int gy = sy; gy < H; gy++) {
                    for (int gx = sx; gx < W; gx++) {
                        if (!reachable[sy][sx][gy][gx])
                            continue;
                        if (gx + 1 < W && C[gy][gx + 1] != '#')
                            reachable[sy][sx][gy][gx + 1] = 1;
                        if (gy + 1 < H && C[gy + 1][gx] != '#')
                            reachable[sy][sx][gy + 1][gx] = 1;
                    }
                }
            }
        }
        memset(dp, 0, sizeof(dp));
        for (int Y = 1; Y <= H; Y++) {
            for (int X = 1; X <= W; X++) {
                for (int sy = 0; sy + Y <= H; sy++) {
                    for (int sx = 0; sx + X <= W; sx++) {
                        if (C[sy][sx] == '#')
                            continue;
                        if (islower(C[sy][sx])) {
                            for (auto [gy, gx] : hole[C[sy][sx] - 'a']) {
                                if (sx > gx || gx >= sx + X || sy > gy ||
                                    gy >= sy + Y)
                                    continue;
                                if (!reachable[sy][sx][gy][gx] ||
                                    !reachable[gy][gx][sy + Y - 1][sx + X - 1])
                                    continue;
                                if (abs(sx - gx) + abs(sy - gy) == 1) {
                                    chmax(dp[sy][sx][sy + Y - 1][sx + X - 1],
                                          dp[gy][gx][sy + Y - 1][sx + X - 1] +
                                              1);
                                    continue;
                                }
                                for (int i = 0; i < 2; i++) {
                                    int y1 = sy + dy[i], x1 = sx + dx[i];
                                    if (sx > x1 || x1 >= sx + X || sy > y1 ||
                                        y1 >= sy + Y)
                                        continue;
                                    if (!reachable[sy][sx][y1][x1])
                                        continue;
                                    for (int j = 0; j < 2; j++) {
                                        int y2 = gy - dy[j], x2 = gx - dx[j];
                                        if (sx > x2 || x2 >= sx + X ||
                                            sy > y2 || y2 >= sy + Y)
                                            continue;
                                        if (!reachable[y1][x1][y2][x2] ||
                                            !reachable[y2][x2][gy][gx])
                                            continue;
                                        chmax(
                                            dp[sy][sx][sy + Y - 1][sx + X - 1],
                                            dp[y1][x1][y2][x2] +
                                                dp[gy][gx][sy + Y - 1]
                                                  [sx + X - 1] +
                                                1);
                                    }
                                }
                            }
                        }
                        chmax(dp[sy][sx][sy + Y - 1][sx + X - 1],
                              dp[sy][sx + 1][sy + Y - 1][sx + X - 1]);
                        chmax(dp[sy][sx][sy + Y - 1][sx + X - 1],
                              dp[sy + 1][sx][sy + Y - 1][sx + X - 1]);
                    }
                }
            }
        }
        if (reachable[0][0][H - 1][W - 1]) {
            cout << dp[0][0][H - 1][W - 1] << '\n';
        } else {
            cout << -1 << '\n';
        }
    }
}
accuracy: 1
time_ms: 160
memory_kb: 36432
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define FOR(i, s, t) for (int i = (int)(s); i < (int)(t); ++i)


int H, W;
vector<string> C(H);
vector<vector<pair<int, int>>> holes(26);
map<tuple<int, int, int, int>, int> memo;

int rec(int i1, int j1, int i2, int j2) {
    if (memo.count({i1, j1, i2, j2})) return memo[{i1, j1, i2, j2}];
    if (i1 > i2 || j1 > j2 || i2 >= H || j2 >= W || C[i1][j1] == '#') return -1e9;
    if (i1 == i2 && j1 == j2) return 0;
    int res = -1e9;
    if ('a' <= C[i1][j1] && C[i1][j1] <= 'z') {
        for (auto [i, j] : holes[C[i1][j1] - 'a']) {
            if ((i1 == i && j1 + 1 == j) || (i1 + 1 == i && j1 == j)) {
                res = max(res, rec(i, j, i2, j2) + 1);
            } else if (i1 <= i && i <= i2 && j1 <= j && j <= j2) {
                res = max(res,
                    max(
                        max(rec(i1+1, j1, i-1, j), rec(i1+1, j1, i, j-1)),
                        max(rec(i1, j1+1, i-1, j), rec(i1, j1+1, i, j-1))
                    ) + rec(i, j, i2, j2) + 1
                );
            }
        }
    }
    res = max(res, max(rec(i1+1, j1, i2, j2), rec(i1, j1+1, i2, j2)));
    return memo[{i1, j1, i2, j2}] = res;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(15);

    while (true) {
        cin >> H >> W;
        if (H == 0 && W == 0) break;
        C.resize(H);
        for (auto& x : C) cin >> x;
        holes.assign(26, vector<pair<int, int>>());
        memo.clear();
        FOR(i, 0, H) {
            FOR(j, 0, W) {
                if ('A' <= C[i][j] && C[i][j] <= 'Z') {
                    holes[C[i][j] - 'A'].push_back({i, j});
                }
            }
        }
        cout << max(-1, rec(0, 0, H-1, W-1)) << "\n";
    }
}
accuracy: 1
time_ms: 80
memory_kb: 12840
final_rank: 1

code_snippet: #pragma GCC ta g("avx2")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
#define DEBUG
#ifdef DEBUG
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    os << '{';
    for(int i = 0; i < (int)v.size(); i++) {
        if(i) { os << ','; }
        os << v[i];
    }
    os << '}';
    return os;
}
void debugg() { cerr << endl; }
template <class T, class... Args>
void debugg(const T &x, const Args &... args) {
    cerr << " " << x;
    debugg(args...);
}
#define debug(...)                                                             \
    cerr << __LINE__ << " [" << #__VA_ARGS__ << "]: ", debugg(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif
using namespace std;
typedef long long ll;
typedef vector<ll> vl;
typedef vector<vl> vvl;
typedef vector<char> vc;
typedef vector<string> vs;
typedef vector<bool> vb;
typedef vector<double> vd;
typedef pair<ll,ll> P;
typedef pair<int,int> pii;
typedef vector<P> vpl;
typedef tuple<ll,ll,ll> tapu;
#define rep(i,n) for(int i=0; i<(n); i++)
#define REP(i,a,b) for(int i=(a); i<(b); i++)
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
const int inf = (1<<30)-1;
const ll linf = 1LL<<61;
const int MAX = 510000;
int dy[8] = {0,1,0,-1,1,-1,-1,1};
int dx[8] = {-1,0,1,0,1,-1,1,-1};
const double pi = acos(-1);
const double eps = 1e-7;
template<typename T1,typename T2> inline bool chmin(T1 &a,T2 b){
	if(a>b){
		a = b; return true;
	}
	else return false;
}
template<typename T1,typename T2> inline bool chmax(T1 &a,T2 b){
	if(a<b){
		a = b; return true;
	}
	else return false;
}
template<typename T> inline void print(T &a){
    int sz = a.size();
    for(auto itr = a.begin(); itr != a.end(); itr++){
		cout << *itr;
        sz--;
        if(sz) cout << " ";
	}
    cout << "\n";
}
template<typename T1,typename T2> inline void print2(T1 a, T2 b){
	cout << a << " " << b << "\n";
}
template<typename T1,typename T2,typename T3> inline void print3(T1 a, T2 b, T3 c){
	cout << a << " " << b << " " << c << "\n";
}
void mark() {cout << "#" << "\n";}
ll pcount(ll x) {return __builtin_popcountll(x);}
//#include <atcoder/maxflow>
//using namespace atcoder;
//const int mod = 1e9 + 7;
const int mod = 998244353;

int dp[55][55][55][55];
bool v[55][55][55][55];

int main(){
    while(1){
        int h,w; cin >> h >> w;
        if(!h) break;
        vs s(h); rep(i,h) cin >> s[i];
        rep(i,h) rep(j,w) rep(k,h) rep(l,w){
            dp[i][j][k][l] = -inf; 
            v[i][j][k][l] = 0;
        }
        map<char,vpl> pos;
        rep(i,h) rep(j,w){
            if(isupper(s[i][j])) pos[s[i][j]+'a'-'A'].emplace_back(i,j);
        }
        auto f = [&](auto &&f, int a, int b, int c, int d) -> int {
            if(a > c || b > d) return -inf;
            if(s[a][b] == '#' || s[c][d] == '#') return -inf;
            if(a==c && b==d) return 0;
            if(v[a][b][c][d]) return dp[a][b][c][d];
            v[a][b][c][d] = true;
            int res = -inf;
            if(islower(s[a][b])){
                int t = -inf;
                for(auto p : pos[s[a][b]]){
                    int y = p.first, x = p.second;
                    if(y>c || x>d) continue;
                    int u = -inf;
                    if(y==c && x==d) u = 0;
                    if(y<c && s[y+1][x] != '#') chmax(u,f(f,y+1,x,c,d));
                    if(x<d && s[y][x+1] != '#') chmax(u,f(f,y,x+1,c,d));
                    if(abs(a-y)+abs(b-x) == 1) chmax(t,u);
                    else{
                        chmax(t,max({f(f,a+1,b,y-1,x),f(f,a+1,b,y,x-1),f(f,a,b+1,y-1,x),f(f,a,b+1,y,x-1)})+u);
                    }
                }
                chmax(res, t+1);
            }
            if(a+1<c && s[a+1][b] != '#') chmax(res, f(f,a+1,b,c,d));
            if(b+1<d && s[a][b+1] != '#') chmax(res, f(f,a,b+1,c,d));
            if(c>a && s[c-1][d] != '#') chmax(res, f(f,a,b,c-1,d));
            if(d>b && s[c][d-1] != '#') chmax(res, f(f,a,b,c,d-1));
            return dp[a][b][c][d] = res;
        };
        int ans = -1;
        chmax(ans, f(f,0,0,h-1,w-1));
        cout << ans << "\n";
    }
}
accuracy: 1
time_ms: 170
memory_kb: 48692
final_rank: 15

code_snippet: /*	author:  Kite_kuma
	created: 2021.09.06 20:00:41 */

// #ifdef LOCAL
// #define _GLIBCXX_DEBUG
// #endif
#include <bits/stdc++.h>
using namespace std;

#pragma region macros

#define foa(s, v) for(auto &s : v)
#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()
#define popcnt(n) __builtin_popcountll((long long)n)

#define REPname(a, b, c, d, e, ...) e
#define rep(...) REPname(__VA_ARGS__, REP3, REP2, REP1, REP0)(__VA_ARGS__)
#define REP0(x) for(int Counter_in_rep_macro = 0; Counter_in_rep_macro < (x); ++Counter_in_rep_macro)
#define REP1(i, x) for(int i = 0; i < (x); ++i)
#define REP2(i, l, r) for(int i = (l); i < (r); ++i)
#define REP3(i, l, r, c) for(int i = (l); i < (r); i += (c))

#define DREPname(a, b, c, d, e, ...) e
#define drep(...) DREPname(__VA_ARGS__, DREP3, DREP2, DREP1)(__VA_ARGS__)
#define DREP1(i, x) for(int i = (x)-1; i >= 0; --i)
#define DREP2(i, l, r) for(int i = (r)-1; i >= (l); --i)
#define DREP3(i, l, r, c) for(int i = (r)-1; i >= (l); i -= (c))

#pragma endregion

#pragma region aliases

using ll = long long;
using ld = long double;
using ull = unsigned long long;
using vi = std::vector<int>;
using vvi = std::vector<std::vector<int>>;
using vvvi = std::vector<std::vector<std::vector<int>>>;
using vll = std::vector<ll>;
using vvll = std::vector<vll>;
using vvvll = std::vector<vvll>;
using pii = std::pair<int, int>;
using pll = std::pair<long long, long long>;
template <class T = ll>
using V = std::vector<T>;
template <class T = ll>
using VV = V<V<T>>;
template <class T = ll>
using VVV = V<V<V<T>>>;
template <class T = ll>
using pqup = std::priority_queue<T, std::vector<T>, std::greater<T>>;
template <class T = ll>
using pqdn = std::priority_queue<T>;

#pragma endregion

#pragma region constants

const int inf = 1e9;
const long long INF = 1e18;
const long double pi = acos(-1);
const char dl = '\n';
const char sp = ' ';
int dx[8] = {1, 0, -1, 0, 1, -1, -1, 1};
int dy[8] = {0, 1, 0, -1, 1, 1, -1, -1};

const int mod_1000000007 = 1000000007;
const int mod_998244353 = 998244353;

#pragma endregion

#pragma region basic_operation

template <class T0, class T1, class T2>
inline bool in_range(T0 x, T1 lef, T2 rig) {
	return ((lef <= x) && (x < rig));
}

template <class T>
inline bool chmin(T &a, T b) {
	if(a > b) {
		a = b;
		return true;
	}
	return false;
}
template <class T>
inline bool chmax(T &a, T b) {
	if(a < b) {
		a = b;
		return true;
	}
	return false;
}

void Yes(bool f = 1) { std::cout << (f ? "Yes" : "No") << '\n'; }
void No() { std::cout << "No\n"; }
void YES(bool f = 1) { std::cout << (f ? "YES" : "NO") << '\n'; }
void NO() { std::cout << "NO\n"; }

template <class T>
void drop(T answer) {
	std::cout << answer << '\n';
	exit(0);
}

void err(bool flag = true) {
	if(!flag) return;
	std::cout << -1 << '\n';
	exit(0);
}

template <class T>
void vout(std::vector<T> const &v, bool vertically = 0) {
	if(vertically) {
		for(auto const &a : v) {
			std::cout << a << '\n';
		}
		return;
	}
	for(auto it = v.begin(); it != v.end(); it++) {
		std::cout << (*it);
		if(it != v.end() - 1) {
			std::cout << ' ';
		}
	}
	std::cout << '\n';
	return;
}

inline void print() { std::cout << '\n'; }
template <class T>
inline void print(T x) {
	std::cout << x << '\n';
	return;
}
template <typename Head, typename... Tail>
void print(Head H, Tail... T) {
	std::cout << H << " ";
	print(T...);
}

template <class T>
void add(std::vector<T> &v, T val) {
	for(auto &a : v) a += val;
	return;
}

template <class T>
T dup(T a, T b) {
	assert(b != 0);
	return (a + b - 1) / b;
}

template <class T>
T greatest_lower_multiple(T x, T d) {
	if(d == 0) return 0;
	if(d < 0) d *= -1;
	T y = x % d;
	if(y < 0) y += d;
	return x - y;
}

template <class T>
T least_upper_multiple(T x, T d) {
	return -greatest_lower_multiple(-x, d);
}

long long POW(long long a, long long n) {
	long long res = 1;
	while(n > 0) {
		if(n & 1) res = res * a;
		a = a * a;
		n >>= 1;
	}
	return res;
}

long long modpow(long long a, long long n, long long mod) {	 // a^n mod
	assert(n >= 0 && mod);
	if(mod == 1) return 0LL;
	long long res = 1;
	a %= mod;
	while(n > 0) {
		if(n & 1) res = res * a % mod;
		a = a * a % mod;
		n >>= 1;
	}
	return res < 0 ? res + mod : res;
}

// return x which satisfies a * x % mod == gcd(a, mod)
// not (mod | a)
long long modinv(long long a, long long mod) {
	long long b = mod, u = 1, v = 0;
	while(b) {
		long long t = a / b;
		a -= t * b;
		std::swap(a, b);
		u -= t * v;
		std::swap(u, v);
	}
	u %= mod;
	if(u < 0) u += mod;
	return u;
}

template <class T>
int lb(const std::vector<T> &a, const T x) {
	return std::distance(a.begin(), std::lower_bound(a.begin(), a.end(), x));
}
template <class T>
int ub(const std::vector<T> &a, const T x) {
	return std::distance(a.begin(), std::upper_bound(a.begin(), a.end(), x));
}
template <class T>
void unq_sort(std::vector<T> &a) {
	std::sort(a.begin(), a.end());
	a.erase(std::unique(a.begin(), a.end()), a.end());
}
template <class T>
std::vector<int> press(std::vector<T> &a) {
	auto vec = a;
	unq_sort(vec);
	std::vector<int> ret;
	for(auto &v : a) ret.push_back(lb(vec, v));
	return ret;
}

#pragma endregion

#pragma region input
#define VEC(type, name, size) \
	vector<type> name(size);  \
	scanner::INPUT(name)
#define VVEC(type, name, h, w)                     \
	vector<vector<type>> name(h, vector<type>(w)); \
	scanner::INPUT(name)
#define INT(...)     \
	int __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LL(...)            \
	long long __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define STR(...)        \
	string __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define CHR(...)      \
	char __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define DBL(...)        \
	double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LD(...)              \
	long double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define TPL3(type0, type1, type2, name)   \
	std::tuple<type0, type1, type2> name; \
	scanner::INPUT(name);
#define TPL4(type0, type1, type2, type3, name)   \
	std::tuple<type0, type1, type2, type3> name; \
	scanner::INPUT(name);

namespace scanner {
template <class T>
void scan(T &a) {
	std::cin >> a;
}

template <class T, class L>
void scan(std::pair<T, L> &p) {
	scan(p.first);
	scan(p.second);
}

template <class T0, class T1, class T2>
void scan(std::tuple<T0, T1, T2> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	scan(t0);
	scan(t1);
	scan(t2);
	p = std::make_tuple(t0, t1, t2);
}

template <class T0, class T1, class T2, class T3>
void scan(std::tuple<T0, T1, T2, T3> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	T3 t3;
	scan(t0);
	scan(t1);
	scan(t2);
	scan(t3);
	p = std::make_tuple(t0, t1, t2, t3);
}

template <class T>
void scan(std::vector<T> &a) {
	for(auto &i : a) scan(i);
}

void INPUT() {}
template <class Head, class... Tail>
void INPUT(Head &head, Tail &... tail) {
	scan(head);
	INPUT(tail...);
}
}  // namespace scanner

template <typename T1, typename T2>
std::istream &operator>>(std::istream &is, std::pair<T1, T2> &p) {
	is >> p.first >> p.second;
	return is;
}
#pragma endregion

#pragma region debug

#pragma region output
template <typename T1, typename T2>
std::ostream &std::operator<<(std::ostream &os, const std::pair<T1, T2> &p) {
	os << p.first << " " << p.second;
	return os;
}
template <class T>
std::ostream &std::operator<<(std::ostream &os, const std::vector<T> &v) {
	for(int i = 0; i < (int)v.size(); i++) {
		if(i) os << " ";
		os << v[i];
	}
	return os;
}
#pragma endregion

#pragma region view

namespace viewer {
void view(const long long e) {
	if(e == INF)
		std::cerr << "INF";
	else if(e == -INF)
		std::cerr << "-INF";
	else
		std::cerr << e;
}

void view(const int e) {
	if(e == inf)
		std::cerr << "inf";
	else if(e == -inf)
		std::cerr << "-inf";
	else
		std::cerr << e;
}

template <typename T>
void view(const T e) {
	std::cerr << e;
}

template <typename T, typename U>
void view(const std::pair<T, U> &p) {
	std::cerr << "(";
	view(p.first);
	std::cerr << ", ";
	view(p.second);
	std::cerr << ")";
}

template <class T0, class T1, class T2>
void view(const std::tuple<T0, T1, T2> &p) {
	std::cerr << "(";
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ")";
}

template <class T0, class T1, class T2, class T3>
void view(const std::tuple<T0, T1, T2, T3> &p) {
	std::cerr << "(";
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ", ";
	view(std::get<3>(p));
	std::cerr << ")";
}

template <typename T>
void view(const std::set<T> &s) {
	if(s.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(auto &t : s) {
		view(t);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::unordered_set<T> &s) {
	if(s.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(auto &t : s) {
		view(t);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::vector<T> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(const auto &e : v) {
		view(e);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::vector<std::vector<T>> &vv) {
	std::cerr << "{\n";
	for(const auto &v : vv) {
		std::cerr << "\t";
		view(v);
		std::cerr << '\n';
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::vector<std::pair<T, U>> &v) {
	std::cerr << "{\n";
	for(const auto &c : v) {
		std::cerr << "\t(";
		view(c.first);
		std::cerr << ", ";
		view(c.second);
		std::cerr << ")\n";
	}
	std::cerr << "}";
}

template <class T0, class T1, class T2>
void view(const std::vector<std::tuple<T0, T1, T2>> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << '{';
	for(const auto &t : v) {
		std::cerr << "\n\t";
		view(t);
		std::cerr << ",";
	}
	std::cerr << "\n}";
}

template <class T0, class T1, class T2, class T3>
void view(const std::vector<std::tuple<T0, T1, T2, T3>> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << '{';
	for(const auto &t : v) {
		std::cerr << "\n\t";
		view(t);
		std::cerr << ",";
	}
	std::cerr << "\n}";
}

template <typename T, typename U>
void view(const std::map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &t : m) {
		std::cerr << "\t[";
		view(t.first);
		std::cerr << "] : ";
		view(t.second);
		std::cerr << '\n';
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::unordered_map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &t : m) {
		std::cerr << "\t[";
		view(t.first);
		std::cerr << "] : ";
		view(t.second);
		std::cerr << '\n';
	}
	std::cerr << "}";
}
}  // namespace viewer

#pragma endregion

// when debugging : g++ foo.cpp -DLOCAL
#ifdef LOCAL
void debug_out() {}
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
	viewer::view(H);
	std::cerr << ", ";
	debug_out(T...);
}
#define debug(...)                                                \
	do {                                                          \
		std::cerr << __LINE__ << " [" << #__VA_ARGS__ << "] : ["; \
		debug_out(__VA_ARGS__);                                   \
		std::cerr << "\b\b]\n";                                   \
	} while(0)
#define dump(x)                                      \
	do {                                             \
		std::cerr << __LINE__ << " " << #x << " : "; \
		viewer::view(x);                             \
		std::cerr << '\n';                           \
	} while(0)

#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif

#pragma endregion

const int len_max = 50;
VV<VV<int>> dp(len_max, vvvi(len_max, vvi(len_max, vi(len_max))));
vvi grid(len_max, vi(len_max));
const int not_done = -1234;

const int wall = 131245;
int solve(int h) {
	int w;
	cin >> w;

	vector<vector<pair<int, int>>> holes(27);
	rep(i, h) rep(j, w) {
		char c;
		cin >> c;
		int &cell = grid[i][j];
		if(c == '.') {
			cell = 0;
		} else if(c == '#') {
			cell = wall;
		} else if(isupper(c)) {
			cell = -(c - 'A' + 1);
			holes[c - 'A' + 1].emplace_back(i, j);
		} else if(islower(c)) {
			cell = c - 'a' + 1;
		} else {
			assert(false);
		}
	}

	foa(a, dp) foa(b, a) foa(c, b) foa(d, c) d = not_done;

	auto dfs = [&](auto dfs, int fx, int fy, int tx, int ty) {
		if(!(0 <= fx && fx <= tx && tx < h)) return -1;
		if(!(0 <= fy && fy <= ty && ty < w)) return -1;
		if(grid[fx][fy] == wall || grid[tx][ty] == wall) return -1;
		int &tmp = dp[fx][fy][tx][ty];
		if(tmp != not_done) return tmp;
		if(fx == tx && fy == ty) return tmp = 0;
		if(fx + fy + 1 == tx + ty) return tmp = int(grid[fx][fy] > 0 && grid[fx][fy] + grid[tx][ty] == 0);
		tmp = -1;

		if(in_range(grid[fx][fy], 1, 27)) {
			for(const auto &[mx, my] : holes[grid[fx][fy]]) {
				if(mx < fx || my < fy || tx < mx || ty < my) continue;

				int former = -1, latter = 0;

				if(fx + fy + 1 == mx + my) {
					former = 0;
				} else {
					rep(fdx, 2) rep(mdx, 2) {
						int fdy = 1 - fdx;
						int mdy = 1 - mdx;
						int val = dfs(dfs, fx + fdx, fy + fdy, mx - mdx, my - mdy);
						chmax(former, val);
					}
					if(former == -1) continue;
				}

				if(mx != tx || my != ty) {
					latter = -1;
					rep(mdx, 2) {
						int mdy = 1 - mdx;
						chmax(latter, dfs(dfs, mx + mdx, my + mdy, tx, ty));
					}
					if(latter == -1) continue;
				}

				chmax(tmp, former + latter + 1);
			}
		}

		chmax(tmp, max({dfs(dfs, fx + 1, fy, tx, ty), dfs(dfs, fx, fy + 1, tx, ty), dfs(dfs, fx, fy, tx - 1, ty),
						dfs(dfs, fx, fy, tx, ty - 1)}));

		return tmp;
	};

	return dfs(dfs, 0, 0, h - 1, w - 1);
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout << std::fixed << std::setprecision(15);
	srand((unsigned)time(NULL));

	int n;
	while(cin >> n && n) {
		cout << solve(n) << dl;
	}

	return 0;
}
accuracy: 1
time_ms: 260
memory_kb: 31824
final_rank: 5

code_snippet: #include <bits/stdc++.h>
using namespace std;

int H, W;
vector<string> C;
vector<vector<vector<vector<int>>>> dp;
vector<vector<vector<vector<bool>>>> seen;
vector<vector<pair<int, int>>> cp;
const int dx1[2] = {1, 0}, dy1[2] = {0, 1}, dx2[2] = {-1, 0}, dy2[2] = {0, -1};

int maine(int x1, int y1, int x2, int y2) {
    if (x1 != clamp(x1, 0, H - 1) || x2 != clamp(x2, 0, H - 1) || y1 != clamp(y1, 0, W - 1) || y2 != clamp(y2, 0, W - 1) || x1 > x2 || y1 > y2)
        return -100000;
    int& ret = dp[x1][y1][x2][y2];
    if (seen[x1][y1][x2][y2])
        return ret;
    seen[x1][y1][x2][y2] = true;
    char c1 = C[x1][y1], c2 = C[x2][y2];
    if (c1 == '#' || c2 == '#')
        return ret = -100000;
    if (x1 == x2 && y1 == y2)
        return ret = 0;
    if (x2 - x1 + y2 - y1 == 1) {
        ret = 0;
        if (isupper(c2) && c2 + 32 == c1)
            ret = 1;
        return ret;
    } else {
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                ret = max(ret, maine(x1 + dx1[i], y1 + dy1[i], x2 + dx2[j], y2 + dy2[j]) + (isupper(c2) && c1 == c2 + 32));
            }
        }
        for (int i = 0; i < 2; i++) {
            ret = max(ret, maine(x1 + dx1[i], y1 + dy1[i], x2, y2));
            ret = max(ret, maine(x1, y1, x2 + dx2[i], y2 + dy2[i]));
        }
    }
    if (islower(c1)) {
        for (auto&& [x3, y3] : cp[c1 - 'a']) {
            if (x1 <= x3 && x3 <= x2 && y1 <= y3 && y3 <= y2)
                ret = max(ret, maine(x1, y1, x3, y3) + maine(x3, y3, x2, y2));
        }
    }
    return ret;
}

int main() {
    while (cin >> H >> W, H) {
        C.resize(H);
        for (auto&& c : C) {
            cin >> c;
        }
        dp = vector(H, vector(W, vector(H, vector<int>(W, -100000))));
        seen = vector(H, vector(W, vector(H, vector<bool>(W, false))));
        cp = vector(26, vector<pair<int, int>>(0));
        for (int x = 0; x < H; x++) {
            for (int y = 0; y < W; y++) {
                auto c = C[x][y];
                if (isupper(c)) {
                    cp[c - 'A'].emplace_back(x, y);
                }
            }
        }
        cout << max(-1, maine(0, 0, H - 1, W - 1)) << endl;
    }
}
accuracy: 1
time_ms: 440
memory_kb: 69420
final_rank: 17

code_snippet: #include<bits/stdc++.h>

using namespace std;

using P = pair<int,int>;

void chmax(int &a, int b){
    a = max(a,b);
}

constexpr int di[] = {1,0};
constexpr int dj[] = {0,1};

int h, w;
vector<string> c;
bool reachable[50][50][50][50];
int dp[50][50][50][50];
vector<vector<P> > pos;

bool isIn(int i, int j){
    return i>=0 && i<h && j>=0 && j<w;
}

int rec(int i1, int j1, int i2, int j2){
    if(dp[i1][j1][i2][j2] != -1) return dp[i1][j1][i2][j2];
    if(i1==i2 && j1==j2) return 0;
    int res = 0;
    for(int k=0;k<2;k++){
        int ni = i1+di[k], nj = j1+dj[k];
        if(isIn(ni,nj) && reachable[ni][nj][i2][j2]){
            chmax(res, rec(ni,nj,i2,j2));
        }
    }
    if(islower(c[i1][j1])){
        int idx = c[i1][j1] - 'a';
        for(auto p : pos[idx]){
            int pi = p.first, pj = p.second;
            if(!reachable[i1][j1][pi][pj] || !reachable[pi][pj][i2][j2]) continue;
            if(abs(i1-pi)+abs(j1-pj)==1){
                chmax(res, rec(pi,pj,i2,j2)+1);
            }else{
                for(int k=0;k<4;k++){
                    int ni1 = i1+di[k%2], nj1 = j1+dj[k%2];
                    int ni2 = pi-di[k/2], nj2 = pj-dj[k/2];
                    if(isIn(ni1,nj1) && isIn(ni2,nj2) && reachable[ni1][nj1][ni2][nj2]){
                        chmax(res, rec(ni1,nj1,ni2,nj2)+rec(pi,pj,i2,j2)+1);
                    }
                }
            }
        }
    }
    return dp[i1][j1][i2][j2] = res;
}

bool solve(){
    cin >> h >> w;
    if(h == 0) return false;
    c.resize(h);
    for(int i=0;i<h;i++){
        cin >> c[i];
    }
    for(int i=0;i<h;i++){
        for(int j=0;j<w;j++){
            for(int k=0;k<h;k++){
                fill(reachable[i][j][k],reachable[i][j][k]+w,false);
                fill(dp[i][j][k],dp[i][j][k]+w,-1);
            }
        }
    }
    for(int i=h-1;i>=0;i--){
        for(int j=w-1;j>=0;j--){
            if(c[i][j] == '#') continue;
            reachable[i][j][i][j] = true;
            if(i+1<h && c[i+1][j] != '#'){
                for(int k=i+1;k<h;k++){
                    for(int l=j;l<w;l++){
                        reachable[i][j][k][l] |= reachable[i+1][j][k][l];
                    }
                }
            }
            if(j+1<w && c[i][j+1] != '#'){
                for(int k=i;k<h;k++){
                    for(int l=j+1;l<w;l++){
                        reachable[i][j][k][l] |= reachable[i][j+1][k][l];
                    }
                }
            }
        }
    }
    pos.clear();
    pos.resize(26);
    for(int i=0;i<h;i++){
        for(int j=0;j<w;j++){
            if(isupper(c[i][j])){
                pos[c[i][j]-'A'].emplace_back(i,j);
            }
        }
    }
    if(reachable[0][0][h-1][w-1]){
        cout << rec(0,0,h-1,w-1) << endl;
    }else{
        cout << -1 << endl;
    }
    return true;
}

int main(){
    while(solve());
    return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 33788
final_rank: 3