use std::fmt;
use std::io::stdin;
use std::io::BufRead;
use std::str;
use std::str::FromStr;
use std::vec::Vec;
use std::collections::VecDeque;

fn main() {
    let stdin = stdin();
    let mut scan = InputScanner::new(stdin.lock(),256);

    let h:usize = scan.next();
    let w:usize = scan.next();

    let mut a:Vec<Vec<char>> = (0..h).map(|_| scan.next::<String>().chars().collect()).collect::<Vec<Vec<char>>>();

    let mut queue = VecDeque::new();

    for y in 0..h {
        for x in 0..w {
            if a[y][x] == '#' {
                queue.push_back((x,y));
            }
        }
    }

    let mut white_count = h * w - queue.len();
    let mut count = 0;

    let mut onmoved = OnMoved::new(w,h);

    'outer: while queue.len() > 0 {
        let mut current = queue.drain(0..).collect::<VecDeque<_>>();

        if white_count == 0 {
            break 'outer;
        }

        while let Some((x,y)) = current.pop_front() {
            onmoved.vh_move(x,y,|x,y| {
                if a[y][x] == '.' {
                    white_count -= 1;
                    a[y][x] = '#';
                    queue.push_back((x,y));
                }
            })
        }

        count += 1;
    }

    println!("{}",count);
}
struct OnMoved {
    w:usize,
    h:usize
}
impl OnMoved {
    pub fn new(w:usize,h:usize) -> OnMoved {
        OnMoved {
            w:w,
            h:h
        }
    }

    pub fn vh_move<F: FnMut(usize,usize)>(&mut self,x:usize,y:usize,f:F) {
        self.vh_move_with_cond(x,y,f,|_,_| true)
    }

    pub fn vh_move_with_cond<F,C>(&mut self,x:usize,y:usize,mut f:F,mut c:C)
        where F: FnMut(usize,usize),
              C: FnMut(usize,usize) -> bool {

        let x = x as i64;
        let y = y as i64;

        for &(dx,dy) in &[(-1,0),(1,0),(0,-1),(0,1)] {
            let x = x + dx;
            let y = y + dy;

            if x >= 0 && x < self.w as i64 && y >= 0 && y < self.h as i64 && c(x as usize,y as usize) {
                let x = x as usize;
                let y = y as usize;

                f(x,y);
            }
        }
    }
}
struct InputScanner<R: BufRead> {
	reader: R,
	buf: Vec<u8>, // Should never be empty
	pos: usize,   // Should never be out of bounds as long as the input ends with '\n'
}
impl<R: BufRead> InputScanner<R> {
	fn new(reader: R, capacity: usize) -> Self {
		InputScanner {
			reader: reader,
			buf: Vec::with_capacity(capacity),
			pos: 0,
		}
	}

	#[inline]
	fn next<T: FromStr>(&mut self) -> T
	where
		T::Err: fmt::Debug,
	{
		if self.buf.is_empty() {
			self._read_next_line();
		}
		let mut start = None;
		loop {
			match (self.buf[self.pos], start.is_some()) {
				(b' ', true) | (b'\n', true) | (b'\r', true) => break,
				(_, true) | (b' ', false) => self.pos += 1,
				(b'\n', false) | (b'\r', false) => self._read_next_line(),
				(_, false) => start = Some(self.pos),
			}
		}
		let target = &self.buf[start.unwrap()..self.pos];
		unsafe { str::from_utf8_unchecked(target) }.parse().unwrap()
	}

	#[inline]
	fn _read_next_line(&mut self) {
		self.pos = 0;
		self.buf.clear();
		if self.reader.read_until(b'\n', &mut self.buf).unwrap() == 0 {
			panic!("Reached EOF");
		}
	}
}



Measured memory_bytes: 40184