
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.IOException;

import java.util.StringTokenizer;

public class ARArray {
    int[]     visits;
    char[]    array;
    int       w;
    int       h;

    public ARArray( InputStream in ) throws IOException {
	BufferedReader rdr = new BufferedReader( new InputStreamReader( in ) );
	boolean gotWidth = false;
	boolean gotHeight = false;
	int offset = 0;

	w = 1;
	h = 1;

	while ( !gotHeight ) {
	    String line = rdr.readLine();

	    if ( line == null ) {
		throw new IOException( "didn't find width and height" );
	    }

	    StringTokenizer tk = new StringTokenizer( line );

	    while ( !gotHeight && tk.hasMoreTokens() ) {
		boolean gotNumber = false;
		int number;

		try {
		    number = Integer.parseInt( tk.nextToken() );
		    gotNumber = true;
		} catch ( NumberFormatException ex ) {
		    continue;
		}

		if ( gotNumber ) {
		    if ( !gotWidth ) {
			w = number;
			gotWidth = true;
		    } else if ( !gotHeight ) {
			h = number;
			gotHeight = true;
		    }
		}
	    }

	    visits = new int[ w * h ];
	    array = new char[ w * h ];

	    if ( tk.hasMoreTokens() ) {
		String remaining = tk.nextToken( "" );
		int ii;

		for ( ii=0; offset < w * h && ii < remaining.length(); ++ii ) {
		    char c = remaining.charAt( ii );
		    if ( Character.isUpperCase( c ) ) {
			array[ offset++ ] = c;
		    }
		}
	    }
	}

	while ( offset < w * h ) {
	    int c = rdr.read();
	    if ( c < 0 ) {
		throw new IOException( "not enough array" );
	    }

	    if ( Character.isUpperCase( (char)c ) ) {
		array[ offset++ ] = (char)c;
	    }
	}
    }

    static final int MAX_PATH_LEN = ( 2 * 1024 * 1024 );
    static final int TIME_LIMIT = ( 8 * 60 );
    static final int REVERSE_PENALTY = 5;

    int scoreMove( int x, int y, char prevChar, int[] visits, int score ) {
	char curChar = array[ y * w + x ];
	int v = (int)visits[ y * w + x ];
	int twoToTheVisits = ( 1 << v );
	int delta;

	if ( twoToTheVisits == 0 ) {
	    return Integer.MIN_VALUE;
	}

	if ( v < Integer.MAX_VALUE ) {
	    ++visits[ y * w + x ];
	}

	delta = 2 - twoToTheVisits;

	if ( curChar < prevChar ) {
	    if ( delta >= 0 ) {
		delta -= REVERSE_PENALTY;
	    } else {
		delta -= REVERSE_PENALTY;
		if ( delta > 0 ) {
		    delta = Integer.MIN_VALUE;
		}
	    }
	}

	if ( score >= 0 ) {
	    score += delta;
	} else {
	    score += delta;
	    if ( score > 0 ) {
		score = Integer.MIN_VALUE;
	    }
	}

	return score;
    }

    public int scorePath( String path, int x, int y, boolean cycle ) {
	int ex = w-1;
	int ey = h-1;
	int lx = 0;
	int ly = 0;
	int mx = 0;
	int my = 0;
	int offset = 0;
	int pathLen = 0;
	boolean countedPathLen = false;
	int score = 1;

	visits[0] = 1;
	for ( int ii=1; ii < w * h; ++ii ) {
	    visits[ii] = 0;
	}

	while ( ( x != ex || y != ey ) && score != Integer.MIN_VALUE ) {
	    char curChar = array[ y*w + x ];

	    if ( offset == path.length() ) {
		if ( !cycle ) {
		    break;
		}

		if ( ( x <= lx && mx < ex )
		||   ( y <= ly && my < ey ) ) {
		    return Integer.MIN_VALUE;
		}

		if ( !countedPathLen ) {
		    countedPathLen = true;
		    if ( score >= 0 ) {
			score -= pathLen;
		    } else {
			score -= pathLen;
			if ( score > 0 ) {
			    score = Integer.MIN_VALUE;
			}
		    }
		}

		offset = 0;

		    //
		    // recalculate lasts and max's
		    //
		lx = x;
		ly = y;

		mx = x;
		my = y;

	    } else {

		switch ( path.charAt( offset++ ) ) {
		case 'N':
		    if ( y > 0 ) {
			--y;
		    }
		    score = scoreMove( x, y, curChar, visits, score );
		    ++pathLen;
		    break;
		case 'S':
		    if ( y < ey ) {
			++y;
		    }
		    score = scoreMove( x, y, curChar, visits, score );
		    ++pathLen;
		    break;
		case 'E':
		    if ( x < ex ) {
			++x;
		    }
		    score = scoreMove( x, y, curChar, visits, score );
		    ++pathLen;
		    break;
		case 'W':
		    if ( x > 0 ) {
			--x;
		    }
		    score = scoreMove( x, y, curChar, visits, score );
		    ++pathLen;
		    break;
		}
	    }

		//
		// recalculate max values
		//
	    if ( x > mx ) {
		mx = x;
	    }
	    if ( y > my ) {
		my = y;
	    }
	}

	if ( !countedPathLen ) {
	    countedPathLen = true;
	    if ( score >= 0 ) {
		score -= pathLen;
	    } else {
		score -= pathLen;
		if ( score > 0 ) {
		    score = Integer.MIN_VALUE;
		}
	    }
	}

	return score;
    }

    public int scorePath( String path, boolean cycle ) {
	return scorePath( path, 0, 0, cycle );
    }

    public int getWidth() {
	return w;
    }

    public int getHeight() {
	return h;
    }
}
