using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static string InputPattern = "InputX";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("2 5");
WillReturn.Add("S.b.b");
WillReturn.Add("a.a.G");
//4
}
else if (InputPattern == "Input2") {
WillReturn.Add("11 11");
WillReturn.Add("S##...#c...");
WillReturn.Add("...#d.#.#..");
WillReturn.Add("..........#");
WillReturn.Add(".#....#...#");
WillReturn.Add("#.....bc...");
WillReturn.Add("#.##......#");
WillReturn.Add(".......c..#");
WillReturn.Add("..#........");
WillReturn.Add("a..........");
WillReturn.Add("d..#...a...");
WillReturn.Add(".#........G");
//14
}
else if (InputPattern == "Input3") {
WillReturn.Add("11 11");
WillReturn.Add(".#.#.e#a...");
WillReturn.Add(".b..##..#..");
WillReturn.Add("#....#.#..#");
WillReturn.Add(".#dd..#..#.");
WillReturn.Add("....#...#e.");
WillReturn.Add("c#.#a....#.");
WillReturn.Add(".....#..#.e");
WillReturn.Add(".#....#b.#.");
WillReturn.Add(".#...#..#..");
WillReturn.Add("......#c#G.");
WillReturn.Add("#..S...#...");
//-1
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
struct JyoutaiDef
{
internal int CurrX;
internal int CurrY;
internal int Level;
}
static void Main()
{
List<string> InputList = GetInputList();
char[,] BanArr = CreateBanArr(InputList.Skip(1));
int UB_X = BanArr.GetUpperBound(0);
int UB_Y = BanArr.GetUpperBound(1);
int StaX = -1, StaY = -1;
for (int X = 0; X <= UB_X; X++) {
for (int Y = 0; Y <= UB_Y; Y++) {
if (BanArr[X, Y] == 'S') {
StaX = X; StaY = Y;
}
}
}
// ワープできる座標のList[文字]なDict
var WarpListDict = new Dictionary<char, List<PointDef>>();
for (int X = 0; X <= UB_X; X++) {
for (int Y = 0; Y <= UB_Y; Y++) {
char CurrChar = BanArr[X, Y];
if ('a' <= CurrChar && CurrChar <= 'z') {
if (WarpListDict.ContainsKey(CurrChar) == false) {
WarpListDict[CurrChar] = new List<PointDef>();
}
PointDef WillAdd;
WillAdd.X = X;
WillAdd.Y = Y;
WarpListDict[CurrChar].Add(WillAdd);
}
}
}
var Que = new Queue<JyoutaiDef>();
JyoutaiDef WillEnque;
WillEnque.CurrX = StaX;
WillEnque.CurrY = StaY;
WillEnque.Level = 0;
Que.Enqueue(WillEnque);
var VisitedSet = new HashSet<int>();
VisitedSet.Add(GetHash(StaX, StaY));
bool FoundAnswer = false;
int Answer = int.MaxValue;
while (Que.Count > 0) {
JyoutaiDef Dequeued = Que.Dequeue();
// クリア判定
if (BanArr[Dequeued.CurrX, Dequeued.CurrY] == 'G') {
FoundAnswer = true;
Answer = Math.Min(Answer, Dequeued.Level);
}
// 枝切り
if (FoundAnswer && Dequeued.Level >= Answer)
continue;
Action<int, int> EnqueueAct = (pNewX, pNewY) =>
{
if (pNewX < 0 || UB_X < pNewX) return;
if (pNewY < 0 || UB_Y < pNewY) return;
if (BanArr[pNewX, pNewY] == '#') return;
int Hash = GetHash(pNewX, pNewY);
if (VisitedSet.Add(Hash)) {
WillEnque.CurrX = pNewX;
WillEnque.CurrY = pNewY;
WillEnque.Level = Dequeued.Level + 1;
Que.Enqueue(WillEnque);
}
};
char CurrChar = BanArr[Dequeued.CurrX, Dequeued.CurrY];
// テレポート移動
if (WarpListDict.ContainsKey(CurrChar)) {
foreach (PointDef EachPoint in WarpListDict[CurrChar]) {
EnqueueAct(EachPoint.X, EachPoint.Y);
}
}
// Removeしておく
WarpListDict.Remove(CurrChar);
// 徒歩移動
EnqueueAct(Dequeued.CurrX, Dequeued.CurrY - 1);
EnqueueAct(Dequeued.CurrX, Dequeued.CurrY + 1);
EnqueueAct(Dequeued.CurrX - 1, Dequeued.CurrY);
EnqueueAct(Dequeued.CurrX + 1, Dequeued.CurrY);
}
if (FoundAnswer) {
Console.WriteLine(Answer);
}
else {
Console.WriteLine(-1);
}
}
struct PointDef
{
internal int X;
internal int Y;
}
static int GetHash(int pX, int pY)
{
return pX * 10000 + pY;
}
////////////////////////////////////////////////////////////////
// IEnumerable<string>をcharの2次元配列に設定する
////////////////////////////////////////////////////////////////
static char[,] CreateBanArr(IEnumerable<string> pStrEnum)
{
var StrList = pStrEnum.ToList();
if (StrList.Count == 0) {
return new char[0, 0];
}
int UB_X = StrList[0].Length - 1;
int UB_Y = StrList.Count - 1;
char[,] WillReturn = new char[UB_X + 1, UB_Y + 1];
for (int Y = 0; Y <= UB_Y; Y++) {
for (int X = 0; X <= UB_X; X++) {
WillReturn[X, Y] = StrList[Y][X];
}
}
return WillReturn;
}
}