#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const long long INF = (1LL<<62);

using Graph = vector<vector<int>>;
using LLPair = pair<LL, LL>;
using WeightedGraph = vector<vector<LLPair>>; //(対向頂点、コスト)
using PriorityQ = priority_queue<LLPair, vector<LLPair>, greater<LLPair>>;

void dijkstra(WeightedGraph &WG, vector<LL> &D, LL s){
	//D初期化
	for(int i=0; i<D.size(); i++){
		D[i] = INF;
	}
	D[s] = 0; //s->sはコスト0で確定

	//キューには(コスト, 頂点)を入れる。sから当該頂点には当該コストあれば行ける。
	//キューにはコストの昇順で入る。
	PriorityQ pq;
	pq.push(make_pair(0, s));
	while(!pq.empty()){
		LL pos = pq.top().second; pq.pop();
		for(auto nv : WG[pos]){
			LL to = nv.first; //頂点posからたどれる頂点
			LL cost = nv.second; //そのコスト
			if(D[to] > D[pos] + cost){
				//sからtoまではD[to]のコストがあれば行ける(最小ではない)
				D[to] = D[pos] + cost; 
				pq.push(make_pair(D[to], to));
			}
		}
	}
	return;
}

//G[0]-G[25]はa-zの仮想頂点とする。
int H, W;
int getpos(int h, int w){ return (26 + (h*W) + w); }
const int dh[4] = {1, 0, -1, 0};
const int dw[4] = {0, 1, 0, -1};
int main(int argc, char* argv[]){
	cin.tie(0);
	ios::sync_with_stdio(false);

	cin >> H >> W;
	WeightedGraph WG(26+(H*W));
	vector<char> V(26 + (H*W));
	int sh, sw, gh, gw;
	for(int i=0; i<H; i++){
		string s; cin >> s;
		for(int j=0; j<W; j++){
			int pos = getpos(i, j);
			char c = s[j];
			V[pos] = c;
			if(c >= 'a' && c <= 'z'){
				int p = c - 'a';
				WG[pos].push_back(make_pair(p, 1));
				WG[p].push_back(make_pair(pos, 0));
			}
			if(c == 'S'){ sh = i; sw = j; }
			if(c == 'G'){ gh = i; gw = j; }
		}
	}

	//各マスからグラフWGを作成する
	for(int h=0; h<H; h++){
		for(int w=0; w<W; w++){
			int pos = getpos(h, w);
			char c = V[pos];
			if(c == '#') continue;

			for(int i=0; i<4; i++){
				int nh = h + dh[i];
				int nw = w + dw[i];
				int npos = getpos(nh, nw);
				if(nh < 0 || nh >= H || nw < 0 || nw >= W) continue;

				char nc = V[npos];
				if(nc == '#') continue;
				if(nc == c && nc >= 'a' && nc <= 'z'){
					; //none
				}
				else{
					WG[pos].emplace_back(make_pair(npos, 1));
					WG[npos].emplace_back(make_pair(pos, 1));
				}
			}
		}
	}

	vector<LL> dist(26+(H*W));
	dijkstra(WG, dist, getpos(sh, sw));
	LL ans = dist[getpos(gh, gw)];
	if(ans == INF){ ans = -1; }
	printf("%lld\n", ans);
	return 0;
}
#include <algorithm>
#include <array>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <string>
#include <vector>

using namespace std;
#define int long long
#define ll long long
#define pii std::pair<int, int>
#define pdd std::pair<double, double>
#define INF (1LL << 33)
#define REP(i, n) for (int i = 0; i < (int)(n); i++)
#define SHOW(p) \
    if (test)   \
        cout << #p " : " << p << endl;
bool test = false;
template <class T>
[[maybe_unused]] bool chmin(T& a, T b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
};
int H, W;
vector<string> a;
vector<vector<int>> dp;
pii s, g;
vector<vector<pii>> tele;
queue<pii> currentq, nextq;

void hoge(int x, int y)
{
    currentq.push({x, y});
    int nx, ny, time = 0;
    while (currentq.size() != 0) {
        pii place = currentq.front();
        currentq.pop();
        nx = place.first, ny = place.second;
        if (test)
            cout << place.first << ", " << place.second << " will be checked." << endl;

        if (chmin(dp.at(place.first).at(place.second), time)) {
            if (test)
                cout << "time updated." << endl;
            char tmp_cc = a.at(nx).at(ny);
            if (tmp_cc == '#') {
            } else {
                if (tmp_cc == '.' || tmp_cc == 'S' || tmp_cc == 'G') {
                } else if (tele.at(static_cast<int>(tmp_cc - 'a')).size() != 0) {
                    for (auto v : tele.at(static_cast<int>(tmp_cc - 'a'))) {
                        if (dp.at(v.first).at(v.second) > time + 1)
                            nextq.push(v);
                    }
                    tele.at(static_cast<int>(tmp_cc - 'a')).clear();
                }
                if (nx + 1 != H)
                    if (dp.at(nx + 1).at(ny) > time + 1)
                        nextq.push({nx + 1, ny});
                if (nx - 1 != -1)
                    if (dp.at(nx - 1).at(ny) > time + 1)
                        nextq.push({nx - 1, ny});
                if (ny + 1 != W)
                    if (dp.at(nx).at(ny + 1) > time + 1)
                        nextq.push({nx, ny + 1});
                if (ny - 1 != -1)
                    if (dp.at(nx).at(ny - 1) > time + 1)
                        nextq.push({nx, ny - 1});
            }
        }
        if (currentq.size() == 0) {
            currentq = nextq;
            while (!nextq.empty()) {
                if (test) {
                    auto next_p = nextq.front();
                    cout << next_p.first << ", " << next_p.second << endl;
                }
                nextq.pop();
            }
            if (test)
                cout << "time : " << time << " finished. next size " << currentq.size() << endl;
            time++;
        }
    }
}
signed main()
{
    //test = true;
    cin >> H >> W;
    a.resize(H);
    tele.resize(static_cast<int>('z' - 'a') + 1);
    REP(i, H)
    {
        cin >> a.at(i);
        REP(j, W)
        {
            char tmp_c = a.at(i).at(j);
            if (tmp_c == 'S') {
                s = {i, j};
            } else if (tmp_c == 'G') {
                g = {i, j};
            } else if (tmp_c != '#' && tmp_c != '.') {
                tele.at(static_cast<int>(tmp_c - 'a')).push_back({i, j});
            }
        }
    }
    dp.resize(H);
    REP(i, H)
    {
        dp.at(i).resize(W, INF);
    }
    hoge(s.first, s.second);
    if (test) {
        REP(i, H)
        {
            REP(j, W)
            {
                cout << dp.at(i).at(j) << " ";
            }
            cout << endl;
        }
    }
    if (dp.at(g.first).at(g.second) == INF) {
        cout << -1 << endl;
    } else {
        cout << dp.at(g.first).at(g.second) << endl;
    }
    return 0;
}
