#include<deque>
#include<queue>
#include<vector>
#include<algorithm>
#include<iostream>
#include<set>
#include<cmath>
#include<tuple>
#include<string>
#include<chrono>
#include<functional>
#include<iterator>
#include<random>
#include<unordered_set>
#include<array>
#include<map>
#include<iomanip>
#include<assert.h>
#include<bitset>
#include<stack>
#include<memory>
using namespace std;
using namespace std::chrono;
typedef long long int llint;
typedef double lldo;
#define mp make_pair
#define mt make_tuple
#define pub push_back
#define puf push_front
#define pob pop_back
#define pof pop_front
#define fir first
#define sec second
#define res resize
#define ins insert
#define era erase
/*cout<<fixed<<setprecision(20);cin.tie(0);ios::sync_with_stdio(false);*/
const llint mod=1000000007;
const llint big=2.19e15+1;
const long double pai=3.141592653589793238462643383279502884197;
const long double eps=1e-15;
template <class T,class U>bool mineq(T& a,U b){if(a>b){a=b;return true;}return false;}
template <class T,class U>bool maxeq(T& a,U b){if(a<b){a=b;return true;}return false;}
llint gcd(llint a,llint b){if(a%b==0){return b;}else return gcd(b,a%b);}
llint lcm(llint a,llint b){if(a==0){return b;}return a/gcd(a,b)*b;}
template<class T> void SO(T& ve){sort(ve.begin(),ve.end());}
template<class T> void REV(T& ve){reverse(ve.begin(),ve.end());}
template<class T>llint LBI(vector<T>&ar,T in){return lower_bound(ar.begin(),ar.end(),in)-ar.begin();}
template<class T>llint UBI(vector<T>&ar,T in){return upper_bound(ar.begin(),ar.end(),in)-ar.begin();}

int main(void){
	//O(HWHlogHW) 良問
	int H,W,i,j,h,k;cin>>H>>W;
	vector<string>mat(H);
	for(i=0;i<H;i++){cin>>mat[i];}
	static int tate[185][185][185];
	static int yoko[185][185][185];
	for(i=0;i<185;i++){
		for(j=0;j<185;j++){
			for(k=0;k<185;k++){
				tate[i][j][k]=-1;
				yoko[i][j][k]=-1;
			}
		}
	}
	for(i=0;i<H;i++){
		yoko[i][W-1][i]=W-1;
		for(j=W-2;j>=0;j--){
			if(mat[i][j]!=mat[i][j+1]){yoko[i][j][i]=j;}
			else{yoko[i][j][i]=yoko[i][j+1][i];}
		}
	}
	for(i=0;i<H;i++){
		for(j=0;j<W;j++){
			int gen=yoko[i][j][i];
			for(k=i+1;k<H;k++){
				if(mat[i][j]!=mat[k][j]){break;}
				mineq(gen,yoko[k][j][k]);
				yoko[i][j][k]=gen;
				//cerr<<"yoko["<<i<<"]["<<j<<"]["<<k<<"]="<<yoko[i][j][k]<<endl;
			}
			//tateも作る
			int bas=j-1;
			for(k=H-1;k>=i;k--){
				if(yoko[i][j][k]==-1){continue;}
				while(bas<yoko[i][j][k]){
					bas++;
					tate[i][j][bas]=k;
				}
			}
			//for(k=j;k<W;k++){cerr<<"tate["<<i<<"]["<<j<<"]["<<k<<"]="<<tate[i][j][k]<<endl;}
		}
	}
	int ans=0;
	while(-1){
		if(yoko[0][0][H-1]==W-1){cout<<ans<<endl;return 0;}
		for(i=0;i<H;i++){
			for(j=0;j<W;j++){
				//dpを更新します
				for(k=i;k<H;k++){
					if(yoko[i][j][k]==-1){break;}
					if(yoko[i][j][k]==W-1){continue;}
					maxeq(yoko[i][j][k],yoko[i][yoko[i][j][k]+1][k]);
				}
				for(k=j;k<W;k++){
					if(tate[i][j][k]==-1){break;}
					if(tate[i][j][k]==H-1){continue;}
					maxeq(tate[i][j][k],tate[tate[i][j][k]+1][j][k]);
				}
				//たてよこmaxeq
				int bas=j-1;
				for(k=H-1;k>=i;k--){
					if(yoko[i][j][k]==-1){continue;}
					while(bas<yoko[i][j][k]){
						bas++;
						maxeq(tate[i][j][bas],k);
					}
				}
				bas=i-1;
				for(k=W-1;k>=j;k--){
					if(tate[i][j][k]==-1){continue;}
					while(bas<tate[i][j][k]){
						bas++;
						maxeq(yoko[i][j][bas],k);
					}
				}
			}
		}
		ans++;
	}
	cout<<ans<<endl;
}

Measured memory_bytes: 49792