In their setting, they had an image and wanted to "squish" away "unimportant" parts during the resize, while maintaining the remaining image information. Their solution was to find "seams" in the image. A seam is a path between opposite edges of an image, using any combination of straight or diagonal moves (in this way, a seam is a "contiguous" strand of pixels but need not be simply a row or column). Their approach was to use a dynamic programming algorithm to find a seam whose removal would cause the least "disturbance" in an image.

Here, your solution could be even easier. You have an "image" made up of "." and non-"." pixels. You want to find and remove any horizontal or vertical seams made up of entirely "." pixels. This can also be done in a simple dynamic programming way.

This is because the diagonal dots constitute a seam. From your example, it is possible that you have the following unwritten rule: If two X's do not start out adjacent, then they should not become adjacent as a result of seam removal. To accomplish this, you can simply add a "buffer" around the X's:

I used ":" as the "buffer" -- You only need to add buffer space on the east & south sides of every initial "X", instead of around all sides. Adding buffer around all sides would prevent distant X's from getting squished to within 2 cells of each other.
Now removing the horizontal seams as well would result in your example output.