#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define REP(i, n) for (int i = 0; i < (n); ++i)
#define SORT(v) sort((v).begin(), (v).end())
#define RSORT(v) sort((v).rbegin(), (v).rend())
const ll MOD = 1000000007;
const ll nmax = 8;
const ll INF = 1e9;
bool graph[nmax][nmax];

class UnionFind
{
public:
    vector<ll> Parent;

    UnionFind(ll N)
    {
        Parent = vector<ll>(N, -1);
    }
    ll find(ll A)
    {
        if (Parent[A] < 0)
            return A;
        return Parent[A] = find(Parent[A]);
    }

    ll size(ll A)
    {
        return -Parent[find(A)];
    }

    bool Union(ll A, ll B)
    {
        A = find(A);
        B = find(B);
        if (A == B)
        {
            return false;
        }
        if (size(A) < size(B))
            swap(A, B);

        Parent[A] += Parent[B];
        Parent[B] = A;

        return true;
    }
};

ll gcd(ll a, ll b)
{
    if (b == 0)
        return a;
    return gcd(b, a % b);
}

ll lcm(ll a, ll b)
{
    ll g = gcd(a, b);
    return a / g * b;
}

ll mulMod(ll a, ll b)
{
    return (((a % MOD) * (b % MOD)) % MOD);
}

ll powMod(ll a, ll p)
{
    if (p == 0)
    {
        return 1;
    }
    else if (p % 2 == 0)
    {
        ll half = powMod(a, p / 2);
        return mulMod(half, half);
    }
    else
    {
        return mulMod(powMod(a, p - 1), a);
    }
}
int dist[1010][1010];
bool visited[1010][1010];
queue<pair<int, int>> q;
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
ll H, W;
char A[1010][1010];
void bfs(int x, int y)
{
    visited[x][y] = true;
    q.push(make_pair(x, y));
    while (!q.empty())
    {
        pair<int, int> p = q.front();
        q.pop();
        for (int i = 0; i < 4; i++)
        {
            int newX = p.first + dx[i];
            int newY = p.second + dy[i];
            if (newX < 0 || H < newX || newY < 0 || W < newY)
            {
                continue;
            }
            else
            {
                if (A[newX][newY] == '#' || visited[newX][newY] == true)
                {
                    visited[newX][newY] == true;
                    continue;
                }
                else
                {
                    dist[newX][newY] = min(dist[newX][newY],
                                           dist[p.first][p.second] + 1);
                    visited[newX][newY] = true;
                    q.push(make_pair(newX, newY));
                }
            }
        }
    }
}
// Generated by 1.1.4 https://github.com/kyuridenamida/atcoder-tools  (tips: You use the default template now. You can remove this line by using your custom template)
int main()
{

    scanf("%lld", &H);
    scanf("%lld", &W);

    vector<pair<int, int>> black;
    for (int i = 0; i < H; i++)
    {
        for (int j = 0; j < W; j++)
        {
            char c;
            cin >> c;
            A[i][j] = c;
            if (c == '#')
            {
                dist[i][j] = 0;
                q.push(make_pair(i, j));
            }
            else
            {
                dist[i][j] = INT16_MAX;
            }
        }
    }

    bfs(q.front().first, q.front().second);
    int ans = 0;
    for (int i = 0; i < H; i++)
    {
        for (int j = 0; j < W; j++)
        {
            ans = max(ans, dist[i][j]);
        }
    }
    cout << ans << endl;
    return 0;
}


Measured memory_bytes: 14352