// Arup Guha
// 6/9/2016
// Solution to 2014 South East Regional Problem I: Stamp Stamp

import java.util.*;

public class stampstamp {

    final public static int IMPOSSIBLE = 1000000;

    // Stores the grid and the min/max column #s for each row.
    public static int r;
    public static int c;
    public static boolean[][] grid;
    public static int[][] rowMinMax;
    public static int numStamps;

    public static int[][] cumSum;

    public static void main(String[] args) {

        // Read in the grid.
        Scanner stdin = new Scanner(System.in);
        r = stdin.nextInt();
        c = stdin.nextInt();
        grid = new boolean[r][c];
        cumSum = new int[r][c];
        rowMinMax = new int[r][2];
        numStamps = 0;

        // Convert to boolean and count all # (worst case result)
        for (int i=0; i<r; i++) {
            String line = stdin.next();
            for (int j=0; j<c; j++) {
                if (line.charAt(j) == '#') {
                    numStamps++;
                    grid[i][j] = true;
                }
            }
        }

        // We can do at least this well.
        int res = numStamps;

        // Resize and get the min and max column numbers on each row.
        resize();
        fillCumSum();
        setRowMinMax();

        // Get all possible movement vectors.
        HashSet<Integer> allPossible = getAllPossible();

        // Try each possible movement vector, recording the best answer.
        for (Integer cur: allPossible) {
            
            int[] delta = convertBack(cur);
            res = Math.min(res, evaluate(delta));

            // A tiny optimization.
            if (2*res == numStamps) break;
        }

        // Now, redo the other direction.
        flip();
        fillCumSum();
        setRowMinMax();

        // Get all possible movement vectors.
        allPossible = getAllPossible();

        // Try each possible movement vector, recording the best answer.
        for (Integer cur: allPossible) {    
            int[] delta = convertBack(cur);
            res = Math.min(res, evaluate(delta));
            if (2*res == numStamps) break;
        }

        // Ta da!
        System.out.println(res);
    }

    public static void flip() {
        for (int i=0; i<r; i++) {
            for (int j=0; j<c/2; j++) {
                boolean tmp = grid[i][j];
                grid[i][j] = grid[i][c-1-j];
                grid[i][c-1-j] = tmp;
            }
        }
    }

    public static void fillCumSum() {
        for (int i=0; i<r; i++)
            Arrays.fill(cumSum[i], 0);

        for (int i=0; i<r; i++)
            for (int j=0; j<c; j++)
                if (grid[i][j])
                    cumSum[i][j] = 1;

        // First make each row a cumulative sum.
        for (int i=0; i<r; i++)
            for (int j=1; j<c; j++)
                cumSum[i][j] += cumSum[i][j-1];

        // Now, make each item a cumulative box sum from top left.
        for (int j=0; j<c; j++)
            for (int i=1; i<r; i++)
                cumSum[i][j] += cumSum[i-1][j];
    }

    public static int evaluate(int[] delta) {

        // Helpful for me to have names for these.
        int otherR = Math.abs(delta[0]);
        int boxR = r - otherR;
        int otherC = Math.abs(delta[1]);
        int boxC = c - otherC;

        // Eliminate some choices due to sum.
        if (sumBox(0,0,otherR-1,otherC-1) > 0 || sumBox(boxR, boxC, r-1, c-1) > 0) return IMPOSSIBLE;
        if (2*sumBox(otherR,0,r-1,boxC-1) < numStamps) return IMPOSSIBLE;
        if (2*sumBox(0,otherC,boxR-1,c-1) < numStamps) return IMPOSSIBLE;

        // No overlap case. This is pretty easy, everything has to be a perfect match.
        if (boxR < r/2+1 || boxC < c/2+1) {
            for (int i=0; i<boxR; i++)
                for (int j=otherC; j<boxC; j++)
                    if (grid[i][j] != grid[i-delta[0]][j-delta[1]])
                        return IMPOSSIBLE;

            return sumBox(0,otherC,boxR-1,c-1);
        }

        int res = 0;

        // These are our set of cycles to check, each independent of one another.
        for (int i=otherR; i<r; i++) {
            for (int j=0; j<boxC; j++) {

                if (i<boxR && j >= otherC) continue;

                int x = i, y = j;
                int oneCount = 0;

                // Loop through each item in this chain.
                while (inbounds(x,y)) {

                    // Can't have only one 1 in a row.
                    if (!grid[x][y] && oneCount == 1) return IMPOSSIBLE;

                    // Add the minimum number of stamps for this streak.
                    if (!grid[x][y]) {
                        res = res + (oneCount+1)/2;
                        oneCount = 0;
                    }

                    // Streak continues.
                    else 
                        oneCount++;
					
                    // Move to the next item.
                    x -= otherR;
                    y += otherC;
                }

                // Don't forget the last streak!
                if (oneCount == 1) return IMPOSSIBLE;
                res = res + (oneCount+1)/2;				
            }
	}
		
        // Ta da!
        return res;
    }

    public static boolean inbounds(int x, int y) {
        return x >= 0 && x < r && y >= 0 && y < c;
    }

    public static void resize() {

        int minR = r, maxR = -1, minC = c, maxC = -1;

        // Find min, max row and column.
        for (int i=0; i<r; i++) {
            for (int j=0; j<c; j++) {
                if (grid[i][j]) {
                    minR = Math.min(minR, i);
                    maxR = Math.max(maxR, i);
                    minC = Math.min(minC, j);
                    maxC = Math.max(maxC, j);
                }
            }
        }

        // New Dimensions.
        int nR = maxR-minR+1;
        int nC = maxC-minC+1;

        // Resize and store.
        boolean[][] res = new boolean[nR][nC];
        for (int i=0; i<nR; i++)
            for (int j=0; j<nC; j++)
                res[i][j] = grid[i+minR][j+minC];

        // Now, copy back so we can just use our static variable.
        grid = res;
        r = nR;
        c = nC;
    }

    public static void setRowMinMax() {

        // Initialize to indicate no min or max on this row.
        for (int i=0; i<r; i++) {
            rowMinMax[i][0] = c;
            rowMinMax[i][1] = -1;
        }

        // Now just go through and set these - a bit redundant, but oh well...
        for (int i=0; i<r; i++) {
            for (int j=0; j<c; j++) {
                if (grid[i][j]) {
                    rowMinMax[i][0] = Math.min(rowMinMax[i][0], j);
                    rowMinMax[i][1] = Math.max(rowMinMax[i][1], j);
                }
            }
        }
    }

    // Returns all possible movement vectors from bottom left to top right.
    public static HashSet<Integer> getAllPossible() {

        HashSet<Integer> res = new HashSet<Integer>();

        // Idea - can move to anything on the bottom row.
        // Or, if we go up, max has to match max, or min has to match min.
        for (int i=rowMinMax[r-1][0]+1; i<=rowMinMax[r-1][1]; i++)
            if (grid[r-1][i])
                res.add(convert(0,i-rowMinMax[r-1][0]));

        // Now, match max with max.
        for (int i=r-1; i>0; i--) {
            for (int j=i-1; j>=0; j--) {
                
                // Nothing on this row to match.
                if (rowMinMax[i][1] == -1 || rowMinMax[j][1] == -1 || rowMinMax[j][1]-rowMinMax[i][1] < 0) continue;

                res.add(convert(j-i, rowMinMax[j][1]-rowMinMax[i][1]));
            }
        }

        // Now, match min with min.
        for (int i=r-1; i>0; i--) {
            for (int j=i-1; j>=0; j--) {

                // Nothing on this row to match.
                if (rowMinMax[i][0] == c || rowMinMax[j][0] == c || rowMinMax[j][0]-rowMinMax[i][0] < 0) continue;
                res.add(convert(j-i, rowMinMax[j][0]-rowMinMax[i][0]));
            }
        }
		
        // These are all our possible movement vectors.
        return res;
    }

    // Converts (x,y) to a unique integer where -500 <= x <= 500 and -500 <= y <= 500
    public static int convert(int x, int y) {
        return 2001*(x+500) + (y+500);
    }

    public static int[] convertBack(int code) {
        return new int[]{code/2001-500, code%2001-500};
    }

    // Gets the cumulative sum of # marks in the box from (topR, topC) to (bottomR, bottomC), inclusive.
    public static int sumBox(int topR, int topC, int bottomR, int bottomC) {
        
        // Empty boxes.
        if (topR >= r || topC >= c || topR < 0 || topC < 0) return 0;
        if (bottomR >= r || bottomC >= c || bottomR < 0 || bottomC < 0) return 0;

        // Get 4 quadrants and use inclusion-exclusion.
        int total = cumSum[bottomR][bottomC];
        int subLeft = topC > 0 ? cumSum[bottomR][topC-1] : 0;
        int subRight = topR > 0 ? cumSum[topR-1][bottomC] : 0;
        int addTop = topC > 0 && topR > 0 ? cumSum[topR-1][topC-1] : 0;
        return total - subLeft - subRight + addTop;
    }
}