#include <cstdio>
#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <numeric>
#include <functional>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <memory>
#include <thread>
#include <tuple>

using namespace std;

int n, m;
short hori[2][187][187][187]; // c1, c2, r1
short vert[2][187][187][187]; // r1, r2, c1
char dat[200][200];

int main() {
  scanf("%d%d", &n, &m);
  for (int i = 0; i < n; i++) {
    scanf("%s", dat[i]);
  }

  for (int r2 = 0; r2 < n; r2++) {
    char last = '-';
    int pos = m - 1;
    for (int c = m - 1; c >= 0; c--) {
      if (dat[r2][c] != last) {
        last = dat[r2][c];
        pos = c;
      }
      vert[0][r2][r2][c] = pos;
    }
    for (int r1 = r2 - 1; r1 >= 0; r1--) {
      for (int c = 0; c < m; c++) {
        if (dat[r1][c] == dat[r2][c]) vert[0][r1][r2][c] = min(vert[0][r1][r2 - 1][c], vert[0][r2][r2][c]);
        else vert[0][r1][r2][c] = -1;
      }
    }
  }
  for (int c2 = 0; c2 < m; c2++) {
    char last = '-';
    int pos = n - 1;
    for (int r = n - 1; r >= 0; r--) {
      if (dat[r][c2] != last) {
        last = dat[r][c2];
        pos = r;
      }
      hori[0][c2][c2][r] = pos;
    }
    for (int c1 = c2 - 1; c1 >= 0; c1--) {
      for (int r = 0; r < n; r++) {
        if (dat[r][c1] == dat[r][c2]) hori[0][c1][c2][r] = min(hori[0][c1][c2 - 1][r], hori[0][c2][c2][r]);
        else hori[0][c1][c2][r] = -1;
      }
    }
  }

  for (int ans = 0; ; ans++) {
    int a = ans % 2;
    int b = 1 - a;

    if (vert[a][0][n - 1][0] >= m - 1) {
      printf("%d\n", ans);
      break;
    }
    memcpy(vert[b], vert[a], sizeof(vert[a]));
    memcpy(hori[b], hori[a], sizeof(hori[a]));
    for (int r1 = 0; r1 < n; r1++) {
      for (int r2 = r1; r2 < n; r2++) {
        for (int c = 0; c < m; c++) {
          short reach = vert[a][r1][r2][c];
          if (reach >= c) {
            reach = vert[a][r1][r2][reach + 1];
            vert[b][r1][r2][c] = max(vert[b][r1][r2][c], reach);
          }
        }
        int p = -1;
        for (int c = 0; c < m; c++) {
          while (p + 1 < m) {
            int c2 = max(c, p + 1);
            auto reach = hori[a][c][c2][r1];
            if (reach >= r1 && reach + 1 < n) {
              reach = max(reach, hori[a][c][c2][reach + 1]);
            }
            if (reach < r2) {
              break;
            }
            p = c2;
          }
          if (p >= c) vert[b][r1][r2][c] = max((int)vert[b][r1][r2][c], p);
        }
      }
    }

    for (int c1 = 0; c1 < m; c1++) {
      for (int c2 = c1; c2 < m; c2++) {
        for (int r = 0; r < n; r++) {
          short reach = hori[a][c1][c2][r];
          if (reach >= r) {
            reach = hori[a][c1][c2][reach + 1];
            hori[b][c1][c2][r] = max(hori[b][c1][c2][r], reach);
          }
        }
        int p = -1;
        for (int r = 0; r < n; r++) {
          while (p + 1 < n) {
            int r2 = max(r, p + 1);
            auto reach = vert[a][r][r2][c1];
            if (reach >= c1 && reach + 1 < m) {
              reach = max(reach, vert[a][r][r2][reach + 1]);
            }
            if (reach < c2) {
              break;
            }
            p = r2;
          }
          if (p >= r) hori[b][c1][c2][r] = max((int)hori[b][c1][c2][r], p);
        }
      }
    }
  }
  return 0;
}


Measured memory_bytes: 51328