// Arup Guha
// 3/21/2017
// Solution to 2017 March USACO Silver Problem: Where's Bessie?

import java.util.*;
import java.io.*;

public class where {

	// Directions of movement.
	final public static int[] DX = {-1,0,0,1};
	final public static int[] DY = {0,-1,1,0};

	public static int n;
	public static char[][] grid;

	public static void main(String[] args) throws Exception {

		// Read the grid.
		Scanner stdin = new Scanner(new File("where.in"));
		n = stdin.nextInt();
		grid = new char[n][];
		for (int i=0; i<n; i++)
			grid[i] = stdin.next().toCharArray();

		ArrayList<Integer> all = new ArrayList<Integer>();
		for (int r1=0; r1<n; r1++)
			for (int r2=r1+1; r2<=n; r2++)
				for (int c1=0; c1<n; c1++)
					for (int c2=c1+1; c2<=n; c2++)
						if (valid(r1,r2,c1,c2))
							all.add(1000000*r1+10000*r2+100*c1+c2);
		int res = 0;
		for (int i=0; i<all.size(); i++) {
			boolean ok = true;
			for (int j=0; j<all.size(); j++) {
				if (j == i) continue;
				if (dominates(all.get(j), all.get(i))) {
					ok = false;
					break;
				}
			}
			if (ok) res++;
		}

		// Ta da!
		PrintWriter out = new PrintWriter(new FileWriter("where.out"));
		out.println(res);
		out.close();
		stdin.close();
	}

	// Return true iff the rectangle determined by code op1 covers that determined by code op2.
	public static boolean dominates(int op1, int op2) {
		int[] dim1 = get(op1);
		int[] dim2 = get(op2);
		return dim1[0] <= dim2[0] && dim1[1] >= dim2[1] && dim1[2] <= dim2[2] && dim1[3] >= dim2[3];
	}

	// Decode code as based on how the four dimensions are stored.
	public static int[] get(int code) {
		int[] res = new int[4];
		res[0] = code/1000000;
		res[1] = (code/10000)%100;
		res[2] = (code/100)%100;
		res[3] = code%100;
		return res;
	}


	public static boolean valid(int r1, int r2, int c1, int c2) {

		// Count unique items.
		boolean[] used = new boolean[26];
		int cnt = 0;
		for (int i=r1; i<r2; i++) {
			for (int j=c1; j<c2; j++) {
				if (!used[grid[i][j]-'A']) {
					used[grid[i][j]-'A'] = true;
					cnt++;
				}
			}
		}

		// Need exactly 2.
		if (cnt != 2) return false;

		// Run one flood fill for each letter.
		boolean[][] filled = new boolean[n][n];

		// Store the character we've done before & number of fills.
		char done = '!';
		int numF = 0;

		// Go through the whole space.
		for (int i=r1; i<r2; i++) {
			for (int j=c1; j<c2; j++) {

				// No more fills needed.
				if (numF == 2) break;

				// Filled already.
				if (filled[i][j]) continue;

				// We should fill it.
				if (grid[i][j] != done) {
					floodfill(i, j, grid[i][j], r1, r2, c1, c2, filled);
					numF++;
				}
			}
			if (numF == 2) break;
		}

		// Reset these.
		used = new boolean[26];
		cnt = 0;

		// Go through the whole space again, count number of unique unmarked items.
		for (int i=r1; i<r2; i++) {
			for (int j=c1; j<c2; j++) {

				if (!filled[i][j] && !used[grid[i][j]-'A']) {
					used[grid[i][j]-'A'] = true;
					cnt++;
				}
			}
		}

		// There needs to be precisely one left.
		return cnt == 1;
	}

	public static void floodfill(int x, int y, char c, int r1, int r2, int c1, int c2, boolean[][] filled) {

		// Out of bounds.
		if (x < r1 || x >= r2) return;
		if (y < c1 || y >= c2) return;

		// Wrong char or already been here.
		if (grid[x][y] != c || filled[x][y]) return;

		// Fill this square.
		filled[x][y] = true;

		// Recursively fill around.
		for (int i=0; i<DX.length; i++)
			floodfill(x+DX[i], y+DY[i], c, r1, r2, c1, c2, filled);
	}
}