fn read_line() -> String {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).unwrap();
    s.trim().to_owned()
}

fn reads<T>() -> Vec<T>
where
    T: std::str::FromStr,
    <T as std::str::FromStr>::Err: std::fmt::Debug,
{
    read_line()
        .split_whitespace()
        .map(|w| w.parse().unwrap())
        .collect()
}

// fn check(s1: &str, s2: &str) -> Option<Vec<usize>> {
//     for i in 0..s1.len() {
//         let c1 = s1.chars.nth().unwrap(i);
//         let c2 = s2.chars.nth().unwrap(i);
//     }
// }

fn solve_x(
    cur_x: usize,
    mut used: i32,
    hist_x: &mut [usize],
    hist_y: &[usize],
    bd: &Vec<Vec<char>>,
) -> bool {
    let h = bd.len();
    let w = bd[0].len();
    if cur_x >= w / 2 {
        for y in 0..h / 2 {
            for x in 0..w {
                let c1 = bd[hist_y[y]][hist_x[x]];
                let c2 = bd[hist_y[h - 1 - y]][hist_x[w - 1 - x]];
                if c1 != c2 {
                    return false;
                }
            }
        }
        if h % 2 == 1 {
            for x in 0..w {
                let c1 = bd[hist_y[h / 2]][hist_x[x]];
                let c2 = bd[hist_y[h / 2]][hist_x[w - 1 - x]];
                if c1 != c2 {
                    return false;
                }
            }
        }
        return true;
    }

    let mut x1 = 0;
    for x in 0..w {
        if used & (1 << x) == 0 {
            used |= 1 << x;
            x1 = x;
            break;
        }
    }

    for x2 in 0..w {
        if used & (1 << x2) != 0 {
            continue;
        }

        let mut ok = true;
        for y in 0..h {
            if bd[hist_y[y]][x1] != bd[hist_y[h - 1 - y]][x2] {
                ok = false;
                break;
            }
        }

        if !ok {
            continue;
        }

        used |= 1 << x2;

        hist_x[cur_x] = x1;
        hist_x[w - 1 - cur_x] = x2;

        if solve_x(cur_x + 1, used, hist_x, hist_y, bd) {
            return true;
        }

        used &= !(1 << x2);
    }

    false
}

fn solve_y(cur_y: usize, mut used: i32, hist_y: &mut [usize], bd: &Vec<Vec<char>>) -> bool {
    let h = bd.len();
    if cur_y >= h / 2 {
        let mut hist_x = vec![0; bd[0].len()];
        let w = bd[0].len();

        if w % 2 == 0 {
            return solve_x(0, 0, &mut hist_x, hist_y, bd);
        } else {
            for x1 in 0..w {
                hist_x[w / 2] = x1;
                if solve_x(0, 1 << x1, &mut hist_x, hist_y, bd) {
                    return true;
                }
            }
            return false;
        }
    }

    let mut y1 = 0;
    for y in 0..h {
        if used & (1 << y) == 0 {
            used |= 1 << y;
            y1 = y;
            break;
        }
    }

    for y2 in 0..h {
        if used & (1 << y2) != 0 {
            continue;
        }

        used |= 1 << y2;

        hist_y[cur_y] = y1;
        hist_y[h - 1 - cur_y] = y2;

        if solve_y(cur_y + 1, used, hist_y, bd) {
            return true;
        }

        used &= !(1 << y2);
    }

    false
}

fn main() {
    let (h, w): (usize, usize) = {
        let t = reads();
        (t[0], t[1])
    };

    let bd: Vec<Vec<char>> = (0..h).map(|_| read_line().chars().collect()).collect();

    let mut hist_y = vec![0; h];

    let ans = if h % 2 == 0 {
        solve_y(0, 0, &mut hist_y, &bd)
    } else {
        let mut ret = false;
        for y1 in 0..h {
            hist_y[h / 2] = y1;
            if solve_y(0, 1 << y1, &mut hist_y, &bd) {
                ret = true;
                break;
            }
        }
        ret
    };

    println!("{}", if ans { "YES" } else { "NO" });
}


Measured memory_bytes: 4352