#include "bits/stdc++.h"

#define int long long
#define REP(i, l, r) REPEAT(i,l,r,true) // [l, r)
#define rep(i, n) REP(i,0,n)           // [0, n)
#define REPEAT(i, l, r, condition) for(int i=(condition)?l:r-1;(condition)?i<r:i>=l;(condition)?++i:--i) // false<-[l, r)->true
#define all(e) e.begin(),e.end()
#define rall(e) e.rbegin(),e.rend()
#define pb push_back
#define fs first
#define sc second
#ifdef LOCAL
#define show(...) cerr<<#__VA_ARGS__<<" = ";_DEBUG(__VA_ARGS__)
#define showlr(n, l, r) cerr<<#n<<" = ";for(int i=l;i<r;i++){cerr<<n[i]<<", ";}cerr<<endl // [l, r)
#else
#define show(...)
#define showlr(n, l, r)
#endif

#define YN(condition) puts((condition)?"YES":"NO")
#define Yn(condition) puts((condition)?"Yes":"No")
#define yn(condition) puts((condition)?"yes":"no")
#define YES puts("YES")
#define Yes puts("Yes")
#define yes puts("yes")
#define NO  puts("NO")
#define No  puts("No")
#define no  puts("no")

#define case(i) printf("Case #%lld: ",i)

using namespace std;

using vi=vector<int>;
using pint=pair<int, int>;

struct io {
    io() {
        cin.tie(nullptr);
        ios::sync_with_stdio(false);
        cout.tie(nullptr);
        cout << fixed << setprecision(20);
    }
} io;

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

template<class T>
ostream &operator<<(ostream &os, vector<T> v) {
    os << "{";
    for (T &e:v)os << e << (v.size() - (int) (&e - &v[0]) > 1 ? ", " : "");
    os << "}";
    return os;
}

void _DEBUG() {}

template<typename H, typename... T>
void _DEBUG(H a, T...b) {
    cerr << a << (sizeof...(b) ? "," : "\n");
    _DEBUG(b...);
}

inline void in() {}

template<typename H, typename... T>
void in(H &a, T &... b) {
    cin >> a;
    in(b...);
}

inline void out() {}

template<typename H, typename... T>
void out(H a, T... b) {
    cout << a << (sizeof...(b) ? " " : "\n");
    out(b...);
}

template<class T>
void resz(int n, T &v) { v.resize(n); }

template<class H, class... T>
void resz(int n, H &a, T &... b) {
    a.resize(n);
    resz(n, b...);
}

template<typename V, typename H>
void resize(vector<V> &v, const H a) { v.resize(a); }

template<typename V, typename H, typename... T>
void resize(vector<V> &v, const H &a, const T... b) {
    v.resize(a);
    for (auto &v2:v) resize(v2, b...);
}

const int INF = 1LL << 55;
const int MOD = 1000000007;
const double EPS = 1e-8;

/*------------end of definitions------------*/

int H, W;
vector<vector<char>> grid;
vector<vi> cost;

vi dx = {0, 1, 0, -1}, dy = {1, 0, -1, 0};

bool check(pint p) {
    return 0 <= p.fs && p.fs < H && 0 <= p.sc && p.sc < W;
}

signed main() {

    in(H, W);
    resize(grid, H, W);
    resize(cost, H, W);
    in(grid);

    queue<pint> que;
    rep(y, H) {
        rep(x, W) {
            cost[y][x] = INF;
            if (grid[y][x] == '#') {
                que.push({y, x});
                cost[y][x] = 0;
            }
        }
    }

    while (!que.empty()) {
        pint cur = que.front();
        que.pop();

        rep(i, 4) {
            pint next = {cur.fs + dy[i], cur.sc + dx[i]};
            //show(next.fs, next.sc, check(next));
            if (!check(next) || cost[next.fs][next.sc] != INF) continue;

            cost[next.fs][next.sc] = cost[cur.fs][cur.sc] + 1;
            que.push(next);
        }
    }

    int ans = -INF;
    rep(i, H) {
        ans = max(ans, *max_element(all(cost[i])));
    }
    out(ans);

    return 0;
}


Measured memory_bytes: 25524