#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[20][187][187][187]; // c1, c2, r1
short vert[20][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++) {
    if (vert[ans][0][n - 1][0] >= m - 1) {
      printf("%d\n", ans);
      break;
    }
    memcpy(vert[ans + 1], vert[ans], sizeof(vert[ans]));
    memcpy(hori[ans + 1], hori[ans], sizeof(hori[ans]));
    for (int r1 = 0; r1 < n; r1++) {
      for (int r2 = r1; r2 < n; r2++) {
        for (int c = 0; c < m; c++) {
          short reach = vert[ans][r1][r2][c];
          if (reach >= c) {
            reach = vert[ans][r1][r2][reach + 1];
            vert[ans+1][r1][r2][c] = max(vert[ans+1][r1][r2][c], reach);
          }
          int low = max(c, (int)vert[ans+1][r1][r2][c] + 1), high = m - 1;
          while (low <= high) {
            int mid = (low + high) >> 1;
            int reach = hori[ans][c][mid][r1];
            if (reach >= r1 && reach + 1 < n) {
              reach = hori[ans][c][mid][reach + 1];
            }
            if (reach >= r2) {
              vert[ans + 1][r1][r2][c] = mid;
              low = mid + 1;
            }
            else {
              high = mid - 1;
            }
          }
        }
      }
    }

    for (int c1 = 0; c1 < m; c1++) {
      for (int c2 = c1; c2 < m; c2++) {
        for (int r = 0; r < n; r++) {
          short reach = hori[ans][c1][c2][r];
          if (reach >= r) {
            reach = hori[ans][c1][c2][reach + 1];
            hori[ans+1][c1][c2][r] = max(hori[ans+1][c1][c2][r], reach);
          }
          int low = max(r, (int)hori[ans+1][c1][c2][r] + 1), high = n - 1;
          while (low <= high) {
            int mid = (low + high) >> 1;
            int reach = vert[ans][r][mid][c1];
            if (reach >= c1 && reach + 1 < m) {
              reach = vert[ans][r][mid][reach + 1];
            }
            if (reach >= c2) {
              hori[ans + 1][c1][c2][r] = mid;
              low = mid + 1;
            }
            else {
              high = mid - 1;
            }
          }
        }
      }
    }
  }
  return 0;
}


Measured memory_bytes: 438528