#include <bits/stdc++.h>

#define countof(a) (sizeof(a)/sizeof(*a))

#define vi std::vector<int>
#define vvi std::vector<std::vector<int> >
#define vpi std::vector<pi >
#define pi std::pair<int,int>
#define fi first
#define se second
#define all(n) n.begin(), n.end()

#define FROMTO(var, from, to) for (int var = (from), var##down = ((int)(to)) < ((int)(from));var##down ? (var >= (int)(to)) : (var <= (int)(to));var##down ? var-- : var++)
#define UPTO(var, from, to)   for (int var = (from); var <= ((int)(to)); var++)
#define DOWNTO(var, from, to) for (int var = (from); var >= ((int)(to)); var--)
#define FOR(var, to)		  UPTO(var, 0, (to)-1)
#define DOWN(var, from)	   DOWNTO(var, (from)-1, 0)

#define INIT(var, val) FOR(i,countof(var)) var[i] = val
#define INPUT(var) FOR(i,countof(var)) std::cin >> var[i]
#define INPUT1(var) FOR(i,countof(var)) std::cin >> var[i], var[i]--

#define SORT(v) qsort(v,countof(v),sizeof(*v),int_less)
#define SORTT(v) qsort(v,countof(v),sizeof(*v),int_greater)
#define QSORT(v,b) qsort(v,countof(v),sizeof(*v),b)

typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;

static const int MOD = 1000000007;
static const int INF = ((1<<30)-1);
static const s64 LINF = ((1LL << 62)-1);

struct Comb {
	std::vector<std::vector<s64> > a;
	Comb(int n) { // O(n^2)
		a.resize(n+1);
		FOR(i,n+1) {
			a[i].resize(i+1, 1);
			UPTO(j,1,i-1) a[i][j] = a[i-1][j] + a[i-1][j];
		}
	}
	s64 ncr(int n, int r) {
		assert(n >= 0);
		assert(n <= (int)a.size());
		assert(r >= 0);
		assert(r <= n);
		return a[n][r];
	}
};

#define gc(c) (c)=getchar()
int ri() {
	int c,s=0,r=0;
	for(;;){gc(c);if(c=='-'){s=1;break;}if(c>='0'&&c<='9'){r=c-'0';break;}}
	for(;;){gc(c);if(c<'0'||c>'9')break;r*=10;r+=c-'0';}
	if(s)r=-r;
	return r;
}
s64 rs64() {
	s64 r = 0;
	int c,s = 0;
	for(;;){gc(c);if(c=='-'){s=1;break;}if(c>='0'&&c<='9'){r=c-'0';break;}}
	for(;;){gc(c);if(c<'0'||c>'9')break;r*=10;r+=c-'0';}
	if(s)r=-r;
	return r;
}

int int_less(const void *a, const void *b) {
	return (*(const int*)a - *(const int*)b);
}
int int_greater(const void *a, const void *b) {
	return (*(const int*)b - *(const int*)a);
}
std::string operator*(const std::string &s, int n){ std::string res; FOR(i,n) res += s; return res; }

template<class T> std::ostream& operator<<(std::ostream& os, std::vector<T> a) { for (auto i : a) os << i << " "; return os; }
template<class T> std::ostream& operator<<(std::ostream& os, std::set<T> a) { for (auto i : a) os << i << " "; return os; }
template<class A, class B> std::ostream& operator<<(std::ostream&os, std::pair<A,B> a) { os << a.fi << "," << a.se; return os; }


struct Graph {
	int n;
	std::vector<std::vector<std::pair<int, s64> > > hen;

	// result of Warshall–Floyd
	std::vector<std::vector<s64> > WF;
	bool wf_completed = false;

	// result of dijkstra and Bellman-Ford/SPFA
	std::vector<std::vector<s64> > SSSP;
	std::set<int> sssp_completed;

	// result of dfs(reachable check)
	std::vector<std::vector<bool> > DFS;
	std::set<int> dfs_completed;

	Graph(int n) { // O(N)
		hen.resize(n, std::vector<std::pair<int, s64> >());
		SSSP.resize(n, std::vector<s64>());
		DFS.resize(n, std::vector<bool>());
		this->n = n;
	}

	// return the position of negative cycle(-1 if none)
	int bf(int k) {
		sssp_completed.insert(k);
		SSSP[k].resize(n,LINF);
		SSSP[k][k] = 0;
		FOR(i,n-1) {
			bool updated = false;
			FOR(j,n) {
				for (auto l : hen[j]) {
					if (SSSP[k][j] != LINF && SSSP[k][l.fi] > SSSP[k][j] + l.se)
						SSSP[k][l.fi] = SSSP[k][j] + l.se, updated = true;
				}
			}
			if (!updated) break;
		}

		FOR(j,n) {
			for (auto l : hen[j]) {
				if (SSSP[k][j] != LINF && SSSP[k][l.fi] > SSSP[k][j] + l.se) {
					return j; // negative loop
				}
			}
		}
		return -1;
	}

	int spfa(int k) {
		std::vector<bool> inque(n,false);
		std::vector<int> updated(n,0);

		sssp_completed.insert(k);
		SSSP[k].resize(n,LINF);
		SSSP[k][k] = 0;

		std::queue<int> que; inque[k] = true;que.push(k);
		while (que.size()) {
			int i = que.front(); que.pop();
			for (auto j : hen[i]) {
				if (SSSP[k][j.fi] > SSSP[k][i] + j.se) {
					SSSP[k][j.fi] = SSSP[k][i] + j.se;
					updated[j.fi]++;
					if (updated[j.fi] > n) return j.fi;
					if (!inque[j.fi]) {
						inque[j.fi] = true;
						que.push(j.fi);
					}
				}
			}
			inque[i] = false;
		}
		return -1;
	}

	void dij(int k) { // O(n+mlogm)
		SSSP[k].resize(n);
		fill(all(SSSP[k]), LINF);
		SSSP[k][k] = 0;
		using T = std::pair<s64, int>;
		std::priority_queue<T, std::vector<T>, std::greater<T> > que;
		que.push({0,k});
		while(que.size()) {
			auto i = que.top(); que.pop();
			for (auto j : hen[i.se]) {
				if (SSSP[k][j.fi] > i.fi + j.se) {
					SSSP[k][j.fi] = i.fi + j.se;
					que.push({SSSP[k][j.fi], j.fi});
				}
			}
		}
		sssp_completed.insert(k);
	}

	void wf() { // O(n^3)
		WF.resize(n);
		fill(all(WF), std::vector<s64>(n,LINF));
		FOR(i,n) {
			WF[i][i] = 0;
			for (auto j : hen[i]) {
				WF[i][j.fi] = j.se;
			}
		}
		FOR(k,n) {
			FOR(i,n) {
				FOR(j,n) {
					if (WF[i][k] != LINF && WF[k][j] != LINF) {
						WF[i][j] = std::min(WF[i][j], WF[i][k] + WF[k][j]);
					}
				}
			}
		}
		wf_completed = true;
	}

	void dfs(int from) { // O(N+M)
		DFS[from].resize(n, false);
		DFS[from][from] = true;
		std::queue<int> que; que.push(from);
		while(que.size()) {
			auto i = que.front(); que.pop();
			for (auto j : hen[i]) {
				if (!DFS[from][j.fi]) {
					DFS[from][j.fi] = true;
					que.push(j.fi);
				}
			}
		}
		dfs_completed.insert(from);
	}

	bool reachable(int from, int to) {
		assert(dfs_completed.count(from));
		return DFS[from][to];
	}

	std::pair<bool,s64> shortest(int from, int to) {
		assert(wf_completed || sssp_completed.count(from));
		if (wf_completed)
			return {WF[from][to] != LINF, WF[from][to]};
		else if (sssp_completed.count(from))
			return {SSSP[from][to] != LINF, SSSP[from][to]};
		else return {false, 0}; // should not reached
	}

	void add_hen(int from, int to, s64 cost, bool muki) {
		hen[from].push_back({to, cost});
		if (!muki) hen[to].push_back({from, cost});
	}
};
// Graph.dfs : O(N)
// Graph.dij : O(N+MlogM)
// Graph.bf  : O(NM)
// Graph.spfa: O(NM) faster
// Graph.wf  : O(N^3)
// Graph.reachable
// Graph.shortest
std::pair<int,int> d[] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int main() {
	int h = ri(),w = ri();
	std::string s[h];
	FOR(i,h) std::cin >> s[i];
	
	std::queue<std::pair<int,int> > que;
	std::vector<std::vector<int> > res(h, std::vector<int>(w, INF));
	FOR(i,h) FOR(j,w) if (s[i][j] == '#') que.push({i, j}), res[i][j] = 0;
	while (que.size()) {
		auto i = que.front(); que.pop();
		for (auto j : d) {
			int new_x = i.first + j.first;
				int new_y = i.second + j.second;
			if (new_x < 0 || new_x >= h) continue;
		if (new_y < 0 || new_y >= w) continue;
			if (res[new_x][new_y] == INF) {
				res[new_x][new_y] = res[i.first][i.second] + 1;
				que.push({new_x, new_y});
			}
		}
	}
	int ans = 0;
	FOR(i,h) FOR(j,w) ans = std::max(ans, res[i][j]);
	std::cout << ans << std::endl;
	return 0;
}


Measured memory_bytes: 13456