using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using CompLib.Collections;
using CompLib.Util;

public class Program
{
    int H, W;
    char[][] C;

    List<int>[][] ls;
    public void Solve()
    {
        var sc = new Scanner();
        // 1個の列
        // コスト0
        H = sc.NextInt();
        W = sc.NextInt();

        C = new char[H][];
        for (int i = 0; i < H; i++)
        {
            C[i] = sc.NextCharArray();
        }

        ls = new List<int>[W][];
        for (int i = 0; i < W; i++)
        {
            ls[i] = new List<int>['z' + 1];
            for (char c = 'a'; c <= 'z'; c++)
            {
                ls[i][c] = new List<int>();
            }
            for (int j = 0; j < H; j++)
            {
                ls[i][C[j][i]].Add(j);
            }
        }


        Console.WriteLine(Calc(0, W));
    }

    long Calc(int l, int r)
    {
        if (r - l <= 1) return 0;

        int m = (l + r) / 2;
        long left = Calc(l, m);
        long right = Calc(m, r);

        // mの2つ
        // m-1,m

        long[,] cost = new long[H + 1, H + 1];
        for (int ll = 0; ll <= H; ll++)
        {
            // 一致
            int cnt = 0;
            for (int i = 0; i + ll < H; i++)
            {
                if (C[i][m - 1] == C[i + ll][m]) cnt++;
            }
            for (int i = 0; i + ll < H; i++)
            {
                cost[i + ll, i] = cnt;
                // 一番下
                if (C[H - 1 - i - ll][m - 1] == C[H - i - 1][m]) cnt--;
            }
        }

        for (int rr = 0; rr <= H; rr++)
        {
            int cnt = 0;
            for (int i = 0; i + rr < H; i++)
            {
                if (C[i + rr][m - 1] == C[i][m]) cnt++;
            }

            for (int i = 0; i + rr < H; i++)
            {
                cost[i, i + rr] = cnt;
                if (C[H - i - 1][m - 1] == C[H - 1 - i - rr][m]) cnt--;
            }
        }

        long[,] dp = new long[H + 1, H + 1];
        for (int i = 0; i <= H; i++)
        {
            for (int j = 0; j <= H; j++)
            {
                dp[i, j] = long.MaxValue;
            }
        }

        dp[0, 0] = 0;
        var q = new Queue<(int left, int right)>();
        q.Enqueue((0, 0));
        while (q.Count > 0)
        {
            var d = q.Dequeue();
            if (d.left == H || d.right == H) continue;
            long cur = dp[d.left, d.right];
            long c = cost[d.left, d.right];
            if (d.left + 1 <= H && dp[d.left + 1, d.right] > cur + c)
            {
                dp[d.left + 1, d.right] = cur + c;
                q.Enqueue((d.left + 1, d.right));
            }

            if (d.right + 1 <= H && dp[d.left, d.right + 1] > cur + c)
            {
                dp[d.left, d.right + 1] = cur + c;
                q.Enqueue((d.left, d.right + 1));
            }
        }

        long min = long.MaxValue;
        for (int i = 0; i <= H; i++)
        {
            min = Math.Min(min, dp[i, H]);
            min = Math.Min(min, dp[H, i]);
        }

        return left + min + right;
    }

    public static void Main(string[] args) => new Program().Solve();
}

// https://bitbucket.org/camypaper/complib
namespace CompLib.Collections
{
    using System;
    using System.Collections.Generic;

    #region PriorityQueue

    /// <summary>
    /// 指定した型のインスタンスを最も価値が低い順に取り出すことが可能な可変サイズのコレクションを表します．
    /// </summary>
    /// <typeparam name="T">優先度付きキュー内の要素の型を指定します．</typeparam>
    /// <remarks>内部的にはバイナリヒープによって実装されています．</remarks>
    public class PriorityQueue<T>
    {
        readonly List<T> heap = new List<T>();
        readonly Comparison<T> cmp;

        /// <summary>
        /// デフォルトの比較子を使用してインスタンスを初期化します．
        /// </summary>
        /// <remarks>この操作は O(1) で実行されます．</remarks>
        public PriorityQueue()
        {
            cmp = Comparer<T>.Default.Compare;
        }

        /// <summary>
        /// デリゲートで表されるような比較関数を使用してインスタンスを初期化します．
        /// </summary>
        /// <param name="comparison"></param>
        /// <remarks>この操作は O(1) で実行されます．</remarks>
        public PriorityQueue(Comparison<T> comparison)
        {
            cmp = comparison;
        }

        /// <summary>
        /// 指定された比較子を使用してインスタンスを初期化します．
        /// </summary>
        /// <param name="comparer"></param>
        /// <remarks>この操作は O(1) で実行されます．</remarks>
        public PriorityQueue(IComparer<T> comparer)
        {
            cmp = comparer.Compare;
        }

        /// <summary>
        /// 優先度付きキューに要素を追加します．
        /// </summary>
        /// <param name="item">優先度付きキューに追加される要素</param>
        /// <remarks>最悪計算量 O(log N) で実行されます．</remarks>
        public void Enqueue(T item)
        {
            var pos = heap.Count;
            heap.Add(item);
            while (pos > 0)
            {
                var par = (pos - 1) / 2;
                if (cmp(heap[par], item) <= 0)
                    break;
                heap[pos] = heap[par];
                pos = par;
            }

            heap[pos] = item;
        }

        /// <summary>
        /// 優先度付きキューから最も価値が低い要素を削除し，返します．
        /// </summary>
        /// <returns>優先度付きキューから削除された要素．</returns>
        /// <remarks>最悪計算量 O(log N) で実行されます．</remarks>
        public T Dequeue()
        {
            var ret = heap[0];
            var pos = 0;
            var x = heap[heap.Count - 1];

            while (pos * 2 + 1 < heap.Count - 1)
            {
                var lch = pos * 2 + 1;
                var rch = pos * 2 + 2;
                if (rch < heap.Count - 1 && cmp(heap[rch], heap[lch]) < 0) lch = rch;
                if (cmp(heap[lch], x) >= 0)
                    break;
                heap[pos] = heap[lch];
                pos = lch;
            }

            heap[pos] = x;
            heap.RemoveAt(heap.Count - 1);
            return ret;
        }

        /// <summary>
        ///  優先度付きキューに含まれる最も価値が低い要素を削除せずに返します．
        /// </summary>
        /// <returns>優先度付きキューに含まれる最も価値が低い要素．</returns>
        /// <remarks>この操作は O(1) で実行されます．</remarks>
        public T Peek()
        {
            return heap[0];
        }

        /// <summary>
        /// 優先度付きキュー内の要素の数を取得します．
        /// </summary>
        /// <returns>優先度付キュー内にある要素の数</returns>
        /// <remarks>最悪計算量 O(1) で実行されます．</remarks>
        public int Count
        {
            get { return heap.Count; }
        }

        /// <summary>
        /// 優先度付きキュー内に要素が存在するかどうかを O(1) で判定します．
        /// </summary>
        /// <returns>優先度付キュー内にある要素が存在するならば true，そうでなければ　false．</returns>
        /// <remarks>この操作は O(1) で実行されます．</remarks>
        public bool Any()
        {
            return heap.Count > 0;
        }

        /// <summary>
        /// 優先度付きキューに含まれる要素を昇順に並べて返します．
        /// </summary>
        /// <remarks>この操作は計算量 O(N log N)で実行されます．</remarks>
        public T[] Items
        {
            get
            {
                var ret = heap.ToArray();
                Array.Sort(ret, cmp);
                return ret;
            }
        }
    }

    #endregion
}

namespace CompLib.Util
{
    using System;
    using System.Linq;

    class Scanner
    {
        private string[] _line;
        private int _index;
        private const char Separator = ' ';

        public Scanner()
        {
            _line = new string[0];
            _index = 0;
        }

        public string Next()
        {
            if (_index >= _line.Length)
            {
                string s;
                do
                {
                    s = Console.ReadLine();
                } while (s.Length == 0);

                _line = s.Split(Separator);
                _index = 0;
            }

            return _line[_index++];
        }

        public string ReadLine()
        {
            _index = _line.Length;
            return Console.ReadLine();
        }

        public int NextInt() => int.Parse(Next());
        public long NextLong() => long.Parse(Next());
        public double NextDouble() => double.Parse(Next());
        public decimal NextDecimal() => decimal.Parse(Next());
        public char NextChar() => Next()[0];
        public char[] NextCharArray() => Next().ToCharArray();

        public string[] Array()
        {
            string s = Console.ReadLine();
            _line = s.Length == 0 ? new string[0] : s.Split(Separator);
            _index = _line.Length;
            return _line;
        }

        public int[] IntArray() => Array().Select(int.Parse).ToArray();
        public long[] LongArray() => Array().Select(long.Parse).ToArray();
        public double[] DoubleArray() => Array().Select(double.Parse).ToArray();
        public decimal[] DecimalArray() => Array().Select(decimal.Parse).ToArray();
    }
}


Measured memory_bytes: 43760