use std::io;
use std::io::prelude::*;
use std::str::FromStr;


struct Reader{
    buf: Vec<String>
}
impl Reader {
    fn _readbuf(&mut self, input: &mut io::Lines<std::io::StdinLock>) {
        if self.buf.len() > 0 {
            return;
        }
        let line = &input.next().unwrap().unwrap();
        for ln in line.split(' ') {
            self.buf.push(ln.to_string())
        }
    }
    fn i32_next(&mut self, input: &mut io::Lines<std::io::StdinLock>) -> i32 {
        self._readbuf(input);
        let a = i32::from_str(&self.buf[0]).unwrap(); 
        self.buf.remove(0);
        return a;
    }
    fn string_next(&mut self, input: &mut io::Lines<std::io::StdinLock>) -> String {
        self._readbuf(input);
        let a = self.buf[0].clone();
        self.buf.remove(0);
        return a;
    }
}


struct DPTable {
   h: usize,
   w: usize,
   color: Vec<char>,
   csum: Vec<i32>,
   dpa: Vec<i32>,
   dpb: Vec<i32>,
}
impl DPTable {
    fn init(&mut self) {
        for _i in 0..(self.h * self.w as usize) {self.color.push(' ');}
        for _i in 0..((self.h + 1) * (self.w + 1) as usize) {self.csum.push(0);}
        for _i in 0..(self.h * self.w * self.h * 2 as usize) {self.dpa.push(0);}
        for _i in 0..(self.h * self.w * self.w * 2 as usize) {self.dpb.push(0);}
    }
    fn to_key(& self, nh: usize, nw: usize, nx: usize, _key: char) -> usize {
        let mut ret = nx;
        if _key == 'a' {
            ret += nh * self.w * self.h;
            ret += nw * self.h;
        } else if _key == 'b' {
            ret += nh * self.w * self.w;
            ret += nw * self.w;
        } else {
            assert!(false);
        }
        ret * 2
    }
    fn add_color(&mut self, nh: usize, nw: usize, c: char) {
        self.color[nh * self.w + nw] = c;
    }
    fn init_csum(&mut self) {
        for i in 0..self.h {
            for j in 0..self.w {
                let mut value = 0;
                if self.color[i * self.w + j] == '#' {
                    value = 1;
                }
                self.csum[(i + 1) * (self.w + 1) + j + 1] = value;
            }
        }
        for i in 0..self.h {
            for j in 0..self.w {
                let pos = (i + 1) * (self.w + 1) + (j + 1);
                let mut value = self.csum[pos];
                value += self.csum[pos - 1];
                value += self.csum[pos - self.w - 1];
                value -= self.csum[pos - self.w - 2];
                self.csum[pos] = value;
            }
        }
        for i in 0..self.h {
            for j in 0..self.w {
                for k in i..self.h {
                    let as_key = self.to_key(i, j, k, 'a');
                    if self.is_same(i, j, k, j) {
                        let mut x = j + 1;
                        while x < self.w {
                            if self.is_same(i, j, k, x as usize) == false {
                                 break;
                            }
                            x += 1;
                        }
                        self.dpa[as_key] = (x - 1) as i32;
                    } else {
                        self.dpa[as_key] = -9999;
                    }
                }
                for k in j..self.w {
                    let as_key = self.to_key(i, j, k, 'b');
                    if self.is_same(i, j, i, k) {
                        let mut x = i + 1;
                        while x < self.h {
                            if self.is_same(i, j, x as usize, k) == false {
                                 break;
                            }
                            x += 1;
                        }
                        self.dpb[as_key] = (x - 1) as i32;
                    } else {
                        self.dpb[as_key] = -9999;
                    }
                }
            }
        }
    }
    fn is_same(& self, nh1: usize, nw1: usize, nh2: usize, nw2: usize) -> bool {
        let mut value = self.csum[(nh2 + 1) * (self.w + 1) + (nw2 + 1)];
        value += self.csum[nh1 * (self.w + 1) + nw1];
        value -= self.csum[nh1 * (self.w + 1) + nw2 + 1];
        value -= self.csum[(nh2 + 1) * (self.w + 1) + nw1];
        if value == 0 {
            true
        } else if value == ((nh2 - nh1 + 1) * (nw2 - nw1 + 1)) as i32 {
            true
        } else {
            false
        }
    }
    fn reset(&mut self) {
        for i in 0..(self.h * self.w * self.h as usize) {
            let keyx = ((i as i32) * 2) as usize; 
            self.dpa[keyx + 1] = self.dpa[keyx];
            self.dpa[keyx] = 0;
        }
        for i in 0..(self.h * self.w * self.w as usize) {
            let keyx = ((i as i32) * 2) as usize; 
            self.dpb[keyx + 1] = self.dpb[keyx];
            self.dpb[keyx] = -9999;
        }
    }
    fn calc(&mut self, nh: usize, nw: usize, nx: usize, _key: char) {
        if _key == 'a' {
            let mut minv = nw as i32 - 1;
            let mut maxv = self.w as i32 - 1;
            let as_key = self.to_key(nh, nw, nx, 'a');
            if self.dpa[as_key + 1] >= 0 {
                minv = self.dpa[as_key + 1];
            }
            while minv < maxv {
                let half = ((minv + maxv + 1) / 2) as usize;
                let mut is_ok = false;
                // tate
                if self.dpa[as_key + 1] >= 0 {
                    let next = self.dpa[as_key + 1] + 1;
                    if next > half as i32 {
                        is_ok = true;
                    } else {
                        let as_key_t = self.to_key(nh, next as usize, nx, 'a');
                        if self.dpa[as_key_t + 1] >= half as i32 {
                            is_ok = true;
                        }
                    }
                }
                // yoko
                let as_key_h = self.to_key(nh, nw, half, 'b'); 
                let point = self.dpb[as_key_h + 1] + 1;
                if point >= 0 {
                    if point > nx as i32 {
                        is_ok = true;
                    } else {
                        let as_key_t = self.to_key(point as usize, nw, half, 'b'); 
                        let point2 = self.dpb[as_key_t + 1];
                        if point2 >= nx as i32 {  
                            is_ok = true;
                        }
                    }
                }
                if is_ok {
                    minv = half as i32;
                } else {
                    maxv = half as i32 - 1;
                }
            }
            if minv >= nw as i32 {
                self.dpa[as_key] = minv;
            } else {
                self.dpa[as_key] = -9999;
            }
        } else {
            let mut minv = nh as i32 - 1;
            let mut maxv = self.h as i32 - 1;
            let as_key = self.to_key(nh, nw, nx, 'b');
            if self.dpb[as_key + 1] >= 0 {
                minv = self.dpb[as_key + 1];
            }
            while minv < maxv {
                let half = ((minv + maxv + 1) / 2) as usize;
                let mut is_ok = false;
                // yoko
                if self.dpb[as_key + 1] >= 0 {
                    let next = self.dpb[as_key + 1] + 1;
                    if next > half as i32 {
                        is_ok = true;
                    } else {
                        let as_key_t = self.to_key(next as usize, nw, nx, 'b');
                        if self.dpb[as_key_t + 1] >= half as i32 {
                            is_ok = true;
                        }
                    }
                }
                // tate
                let as_key_h = self.to_key(nh, nw, half, 'a');
                let point = self.dpa[as_key_h + 1] + 1;
                if point >= 0 {
                    if point > nx as i32 {
                        is_ok = true;
                    } else {
                        let as_key_t = self.to_key(nh, point as usize, half, 'a');
                        let point2 = self.dpa[as_key_t + 1];
                        if point2 >= nx as i32 {  
                            is_ok = true;
                        }
                    }
                }
                if is_ok {
                    minv = half as i32;
                } else {
                    maxv = half as i32 - 1;
                }
            }
            if minv >= nh as i32 {
                self.dpb[as_key] = minv;
            } else {
                self.dpb[as_key] = -9999;
            }
        }
    }
}


fn main() {
    let stdin = io::stdin();
    let mut data = stdin.lock().lines();
    let mut rd = Reader{buf: Vec::new()};
    let h = rd.i32_next(&mut data) as usize;
    let w = rd.i32_next(&mut data) as usize;
    let mut table = DPTable {
        h: h, w: w, color: Vec::new(), csum: Vec::new(),
        dpa: Vec::new(), dpb: Vec::new(),
    };
    table.init();
    for i in 0..h {
        let s = rd.string_next(&mut data);
        for (j, c) in s.chars().enumerate() {
            table.add_color(i, j, c);
        }
    }
    table.reset();
    table.init_csum();
    let mut sol = 0;
    loop {
        let as_key = table.to_key(0, 0, h - 1, 'a');
        if table.dpa[as_key] == (w - 1) as i32 {
            break;
        }
        sol += 1;
        table.reset();
        for i in 0..h {
            for j in 0..w {
                for k in i..h {
                    table.calc(i, j, k, 'a');
                }
                for k in j..w {
                    table.calc(i, j, k, 'b');
                }
            }
        }
    }
    println!("{}", sol);
}

Measured memory_bytes: 121080