import java.util.concurrent.LinkedBlockingDeque
import kotlin.math.max

fun intList() = readLine()?.split(" ")?.map(String::toInt) ?: TODO()
fun main() {
    data class Point(val y: Int, val x: Int, val count: Int)
    val (h, w) = intList()
    val area = mutableListOf<CharArray>()
    val queue = LinkedBlockingDeque<Point>()
    repeat(h) { y ->
        val line = readLine() ?: return
        line.forEachIndexed { x, c ->
            if (line[x] == '#') {
                queue.offer(Point(y, x, 0))
            }
        }
        area.add(line.toCharArray())
    }
    var count = 0
    while (queue.isNotEmpty()) {
        val (y, x, c) = queue.poll()
        if (y - 1 >= 0 && area[y - 1][x] == '.') {
            area[y - 1][x] = '#'
            queue.offer(Point(y - 1, x, c + 1))
            count = max(count, c + 1)
        }
        if (y + 1 < h && area[y + 1][x] == '.') {
            area[y + 1][x] = '#'
            queue.offer(Point(y + 1, x, c + 1))
            count = max(count, c + 1)
        }
        if (x + 1 < w && area[y][x + 1] == '.') {
            area[y][x + 1] = '#'
            queue.offer(Point(y, x + 1, c + 1))
            count = max(count, c + 1)
        }
        if (x - 1 >= 0 && area[y][x - 1] == '.') {
            area[y][x - 1] = '#'
            queue.offer(Point(y, x - 1, c + 1))
            count = max(count, c + 1)
        }
    }
    println(count.toString())
}
