/* #include */
#include"bits/stdc++.h"
#include <sstream>
/* 名前空間 */
using namespace std;

/* #define */
// リピート
#define rpt0(i, n) for(ll i = 0; i < (n); i++)  // リピート 0からn - 1まで
#define rpt1(i, n) for(ll i = 1; i <= (n); i++)  // リピート 1からnまで
#define rptb0(i ,n) for(ll i = (n) - 1; i >= 0; --i) // バックするリピート n - 1から0まで
#define rptb1(i ,n) for(ll i = (n); i >= 1; --i) // バックするリピート nから1まで
// 実用省略
#define pb push_back
#define all(v) v.begin(),v.end()
// 数学
#define PI (acos(-1))  // 円周率pi

/* typedef */
// long系
typedef long long ll;
typedef long double ld;
// vector
typedef vector<int> vint; // intのvector
typedef vector<double> vdouble; // doubleのvector
typedef vector<ll> vll; // llのvector
typedef vector<ld> vld; // ldのvector
typedef vector<pair<ll, ll>> vl_l; // l_lのvector
typedef vector<pair<int, int>> vi_i; // i_iのvector
typedef vector<pair<double, double>> vd_d; // d_dのvector
// pair系
typedef pair<ll, ll> l_l;
typedef pair<int, int> i_i;
typedef pair<double, double> d_d;

/* 入出力 read, write */
/* 変数入力 */
// 1変数
template <typename T>
void read(T& x) {
	cin >> x;
}
// 2変数
template <typename T, typename T0>
void read(T& x, T0& y) {
	cin >> x >> y;
}
// 3変数
template <typename T, typename T0, typename T1>
void read(T& x, T0& y, T1& z) {
	cin >> x >> y >> z;
}
// 4変数
template <typename T, typename T0, typename T1, typename T2>
void read(T& x, T0& y, T1& z, T2& w) {
	cin >> x >> y >> z >> w;
}

/* vector, 配列, pair入力 */
// vector
template <typename T>
void read(vector< T >& oneD, int n) {
	ll n1 = (ll)n;
	rpt0(i, n1)
	{
		ll x;
		read(x);
		oneD.push_back(x);
	}
}
template <typename T>
void read(vector< T >& oneD, ll n) {
	rpt0(i, n)
	{
		ll x;
		read(x);
		oneD.push_back(x);
	}
}
// 配列
template <typename T>
void read(T oneD[], int n) {
	ll n1 = (ll)n;
	rpt1(i, n1) {
		read(oneD[i]);
	}
}
template <typename T>
void read(T oneD[], ll n) {
	rpt1(i, n) {
		read(oneD[i]);
	}
}
// pair
template <typename T, typename T0>
void read(pair< T, T0 >& p) {
	cin >> p.first >> p.second;
}

/* 変数出力 */
// 1変数
template <typename T>
void write(T& x) {

	cout << x << endl;
}
// 2変数
template <typename T, typename T0>
void write(T& x, T0& y) {
	cout << x << " " << y << "\n";
}
// 3変数
template <typename T, typename T0, typename T1>
void write(T& x, T0& y, T1& z) {
	cout << x << " " << y << " " << z << "\n";
}
// 4変数
template <typename T, typename T0, typename T1, typename T2>
void write(T& x, T0& y, T1& z, T2& w) {
	cout << x << " " << y << " " << z << " " << w << "\n";
}

/* vector, 配列, pair出力 */
// vector
template <typename T>
void write(vector< T >& oneD, int n) {
	ll n1 = (ll)n;
	rpt0(i, n1) {
		cout << oneD[i] << " ";
	}
	cout << endl;
}
template <typename T>
void write(vector< T >& oneD, ll n) {
	rpt0(i, n) {
		cout << oneD[i] << " ";
	}
	cout << endl;
}
// 配列
template <typename T>
void write(T oneD[], int n) {
	ll n1 = (ll)n;
	rpt1(i, n1) {
		write(oneD[i]);
	}
	cout << endl;
}
template <typename T>
void write(T oneD[], ll n) {
	rpt1(i, n) {
		write(oneD[i]);
	}
	cout << endl;
}
// pair
template <typename T, typename T0>
void write(pair< T, T0 >& p) {
	cout << p.first << " " << p.second << " ";
	cout << endl;
}



// 八方向への移動ベクトル
const int dx[8] = { 1, 0, -1, 0, 1, 1, -1, -1 };
const int dy[8] = { 0, 1, 0, -1, 1, -1, 1, -1 };
// 右、上、左、下、右上、右下、左上、左下

// 入力
int fh, fw;   // フィールドのfh->縦、fw->横
vector<string> field;   // フィールド

int main(void) {
	// 入力受け取り
	read(fh, fw);
	field.resize(fh);
	for (int h = 0; h < fh; ++h) cin >> field[h];

	// 探索開始
	// BFS のためのデータ構造
	int dist[1000][1000];
	memset(dist, -1, sizeof(dist)); // dist 配列全体を -1 に初期化
	queue<i_i> que; // 訪問保留中の頂点集合

	// 初期条件 (頂点 0 を初期ノードとする)
	rpt0(h, fh) {
		rpt0(w, fw) {
			if (field[h][w] == '#') {
				dist[h][w] = 0;
				i_i sp(h, w);	// スタートの座標をキューにセット
				que.push(sp); // スタートを保留にする
			}
		}
	}

	// BFS 開始 (キューが空になるまで探索を行う)
	while (!que.empty()) {
		i_i v = que.front(); // キューから先頭頂点を取り出す
		que.pop();  // 保留にする

		// v から辿れる頂点をすべて調べる
		for (int dir = 0; dir < 4; ++dir) {     // 方向選択
		// dir = 0 -> 右、1 -> 上、2 -> 左、 3 -> 下
			int nh = v.first + dx[dir];
			int nw = v.second + dy[dir];
			if (nh < 0 || nh >= fh || nw < 0 || nw >= fw) continue;     // 場外アウトの場合
			else if (field[nh][nw] == '#') continue;     // 移動先が壁の場合
			else if (dist[nh][nw] != -1) continue;    // 移動先が探索済みの場合

			// 新たな探索していない頂点 nv について距離情報を更新してキューに追加する
			dist[nh][nw] = dist[v.first][v.second] + 1;
			i_i np(nh, nw);
			que.push(np);
		}
	}

	// 結果出力 (各頂点の頂点 0 からの距離を見る)
	int max = 0;
	for (int h = 0; h < fh; h++) {
		for (int w = 0; w < fw; w++) {
			if (max < dist[h][w]) {
				max = dist[h][w];
			}
		}
	}
	write(max);

	return 0;
}





Measured memory_bytes: 13456