#include<bits/stdc++.h>

using namespace std;

using int64 = long long;
const int mod = 1e9 + 7;

const int64 infll = (1LL << 62) - 1;
const int inf = (1 << 30) - 1;

struct IoSetup {
  IoSetup() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
    cerr << fixed << setprecision(10);
  }
} iosetup;


template< typename T1, typename T2 >
ostream &operator<<(ostream &os, const pair< T1, T2 > &p) {
  os << p.first << " " << p.second;
  return os;
}

template< typename T1, typename T2 >
istream &operator>>(istream &is, pair< T1, T2 > &p) {
  is >> p.first >> p.second;
  return is;
}

template< typename T >
ostream &operator<<(ostream &os, const vector< T > &v) {
  for(int i = 0; i < (int) v.size(); i++) {
    os << v[i] << (i + 1 != v.size() ? " " : "");
  }
  return os;
}

template< typename T >
istream &operator>>(istream &is, vector< T > &v) {
  for(T &in : v) is >> in;
  return is;
}

template< typename T1, typename T2 >
inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }

template< typename T1, typename T2 >
inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }

template< typename T = int64 >
vector< T > make_v(size_t a) {
  return vector< T >(a);
}

template< typename T, typename... Ts >
auto make_v(size_t a, Ts... ts) {
  return vector< decltype(make_v< T >(ts...)) >(a, make_v< T >(ts...));
}

template< typename T, typename V >
typename enable_if< is_class< T >::value == 0 >::type fill_v(T &t, const V &v) {
  t = v;
}

template< typename T, typename V >
typename enable_if< is_class< T >::value != 0 >::type fill_v(T &t, const V &v) {
  for(auto &e : t) fill_v(e, v);
}

template< class T >
struct CumulativeSum2D {
  vector< vector< T > > data;

  CumulativeSum2D(int W, int H) : data(W + 1, vector< int >(H + 1, 0)) {}

  void add(int x, int y, T z) {
    ++x, ++y;
    if(x >= data.size() || y >= data[0].size()) return;
    data[x][y] += z;
  }

  void build() {
    for(int i = 1; i < data.size(); i++) {
      for(int j = 1; j < data[i].size(); j++) {
        data[i][j] += data[i][j - 1] + data[i - 1][j] - data[i - 1][j - 1];
      }
    }
  }

  T query(int sx, int sy, int gx, int gy) {
    return (data[gx][gy] - data[sx][gy] - data[gx][sy] + data[sx][sy]);
  }
};


int main() {
  int H, W;
  cin >> H >> W;
  vector< string > S(H);
  cin >> S;

  auto dpY = make_v< int >(H, H, W);
  auto dpX = make_v< int >(W, W, H);
  fill_v(dpY, -1);
  fill_v(dpX, -1);


  auto sweep = [&]() {
    for(int head = 0; head < H; head++) {
      for(int bottom = head; bottom < H; bottom++) {
        int mid = -1;
        for(int left = 0; left < W; left++) {
          chmax(mid, left - 1);
          while(mid + 1 < W && dpX[left][mid + 1][head] >= bottom) ++mid;
          if(mid >= left) chmax(dpY[head][bottom][left], mid);
        }
      }
    }
    for(int head = 0; head < W; head++) {
      for(int bottom = head; bottom < W; bottom++) {
        int mid = -1;
        for(int left = 0; left < H; left++) {
          chmax(mid, left - 1);
          while(mid + 1 < H && dpY[left][mid + 1][head] >= bottom) ++mid;
          if(mid >= left) chmax(dpX[head][bottom][left], mid);
        }
      }
    }
  };

  CumulativeSum2D< int > sum1(H, W), sum2(H, W);
  for(int i = 0; i < H; i++) {
    for(int j = 0; j < W; j++) {
      sum1.add(i, j, S[i][j] == '#');
      sum2.add(i, j, S[i][j] == '.');
    }
  }
  sum1.build();
  sum2.build();


  for(int head = 0; head < H; head++) {
    for(int bottom = head; bottom < H; bottom++) {
      for(int left = 0; left < W; left++) {
        int ok = left - 1, ng = W;
        while(ng - ok > 1) {
          int mid = (ok + ng) / 2;
          int val1 = sum1.query(head, left, bottom + 1, mid + 1);
          int val2 = sum2.query(head, left, bottom + 1, mid + 1);
          if(val1 == 0 || val2 == 0) ok = mid;
          else ng = mid;
        }
        if(ok >= left) {
          chmax(dpY[head][bottom][left], ok);
        }
      }
    }
  }

  for(int head = 0; head < W; head++) {
    for(int bottom = head; bottom < W; bottom++) {
      for(int left = 0; left < H; left++) {
        int ok = left - 1, ng = H;
        while(ng - ok > 1) {
          int mid = (ok + ng) / 2;
          int val1 = sum1.query(left, head, mid + 1, bottom + 1);
          int val2 = sum2.query(left, head, mid + 1, bottom + 1);
          if(val1 == 0 || val2 == 0) ok = mid;
          else ng = mid;
        }
        if(ok >= left) chmax(dpX[head][bottom][left], ok);
      }
    }
  }
  sweep();

  for(int i = 0; i < 20; i++) {
    if(dpY[0][H - 1][0] >= W - 1) {
      cout << i << endl;
      return 0;
    }
    auto ndpY = dpY;
    auto ndpX = dpX;
    for(int head = 0; head < H; head++) {
      for(int bottom = head; bottom < H; bottom++) {
        for(int left = 0; left < W; left++) {
          if(~dpY[head][bottom][left]) {
            if(dpY[head][bottom][left] + 1 < W) {
              chmax(ndpY[head][bottom][left], dpY[head][bottom][dpY[head][bottom][left] + 1]);
            }
          }
        }
      }
    }
    for(int head = 0; head < W; head++) {
      for(int bottom = head; bottom < W; bottom++) {
        for(int left = 0; left < H; left++) {
          if(~dpX[head][bottom][left]) {
            if(dpX[head][bottom][left] + 1 < H) {
              chmax(ndpX[head][bottom][left], dpX[head][bottom][dpX[head][bottom][left] + 1]);
            }
          }
        }
      }
    }
    ndpY.swap(dpY);
    ndpX.swap(dpX);
    sweep();
  }
}

Measured memory_bytes: 104416