Problem:
Score : 400 points

Problem Statement

There is a grid with H horizontal rows and W vertical columns, and there are obstacles on some of the squares.

Snuke is going to choose one of the squares not occupied by an obstacle and place a lamp on it.
The lamp placed on the square will emit straight beams of light in four cardinal directions: up, down, left, and right.
In each direction, the beam will continue traveling until it hits a square occupied by an obstacle or it hits the border of the grid. It will light all the squares on the way, including the square on which the lamp is placed, but not the square occupied by an obstacle.

Snuke wants to maximize the number of squares lighted by the lamp.

You are given H strings S_i (1 \leq i \leq H), each of length W. If the j-th character (1 \leq j \leq W) of S_i is #, there is an obstacle on the square at the i-th row from the top and the j-th column from the left; if that character is ., there is no obstacle on that square.

Find the maximum possible number of squares lighted by the lamp.

Constraints

1 \leq H \leq 2,000

1 \leq W \leq 2,000

S_i is a string of length W consisting of # and ..

. occurs at least once in one of the strings S_i (1 \leq i \leq H).

Input

Input is given from Standard Input in the following format:

H W
S_1
:
S_H

Output

Print the maximum possible number of squares lighted by the lamp.

Sample Input 1

4 6
#..#..
.....#
....#.
#.#...

Sample Output 1

8

If Snuke places the lamp on the square at the second row from the top and the second column from the left, it will light the following squares: the first through fifth squares from the left in the second row, and the first through fourth squares from the top in the second column, for a total of eight squares.

Sample Input 2

8 8
..#...#.
....#...
##......
..###..#
...#..#.
##....#.
#...#...
###.#..#

Sample Output 2

13

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Sandbox
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var scanner = new Scanner();
            var h = scanner.GetNextAsInt();
            var w = scanner.GetNextAsInt();
            var s = new string[h];

            var sh = Enumerable.Range(0, w).Select(_ => new List<int>()).ToArray();
            var sw = Enumerable.Range(0, h).Select(_ => new List<int>()).ToArray();

            for (var i = 0; i < h; i++)
            {
                var si = scanner.GetNext();
                s[i] = si;
            }

            for (var i = 0; i < h; i++)
            {
                for (var j = 0; j < w; j++)
                {
                    var c = s[i][j];
                    if (c == '#')
                    {
                        sh[j].Add(i);
                        sw[i].Add(j);
                    }
                }
            }

            var max = 0;

            for (var j = 0; j < h; j++)
            {
                var sicount = 0;
                var swl = - 1;
                var swr = w;
                if (sw[j].Count > 0)
                {
                    swr = sw[j][0];
                }

                for (var i = 0; i < w; i++)
                {
                    var shu = - 1;
                    var shd = h;

                    if (sh[i].Count > 0)
                    {
                        var hlb = BinarySearch.LowerBound(sh[i], j);
                        if (hlb < sh[i].Count)
                        {
                            shd = sh[i][hlb];
                        }
                        if (hlb > 0)
                        {
                            shu = sh[i][hlb - 1];
                        }
                    }

                    if (i == swr)
                    {
                        swl = swr;
                        sicount++;

                        if (sicount < sw[j].Count)
                        {
                            swr = sw[j][sicount];
                        }
                        else
                        {
                            swr = w;
                        }

                        continue;
                    }

                    if (shu == i)
                    {
                        continue;
                    }

                    var sum = swr - swl - 1;
                    sum += shd - shu - 1;

                    max = Math.Max(max, sum - 1);
                }
            }

            Console.WriteLine(max);
        }

        #region String Scanner

        public class Scanner
        {
            private const char Separator = ' ';
            private readonly StreamReader _Stream = new StreamReader(Console.OpenStandardInput());
            private int _Pos;
            private string[] _Line = new string[0];

            #region get a element

            public string GetNext()
            {
                // ReSharper disable once InvertIf
                if (this._Pos >= this._Line.Length)
                {
                    // ReSharper disable once PossibleNullReferenceException
                    this._Line = this._Stream.ReadLine().Split(Separator);
                    this._Pos = 0;
                }

                return this._Line[this._Pos++];
            }

            public int GetNextAsInt()
            {
                return int.Parse(this.GetNext());
            }

            public long GetNextAsLong()
            {
                return long.Parse(this.GetNext());
            }

            public double GetNextAsDouble()
            {
                return double.Parse(this.GetNext());
            }

            #endregion

            #region get array

            public string[] ToArrayFromNextLine()
            {
                if (this._Pos >= this._Line.Length)
                {
                    // ReSharper disable once PossibleNullReferenceException
                    this._Line = this._Stream.ReadLine().Split(Separator);
                }

                this._Pos = this._Line.Length;
                return this._Line;
            }

            public int[] ToIntArrayFromNextLine()
            {
                return ToIntArray(this.ToArrayFromNextLine());
            }

            public long[] ToLongArrayFromNextLine()
            {
                return ToLongArray(this.ToArrayFromNextLine());
            }

            public double[] ToDoubleArrayFromNextLine()
            {
                return ToDoubleArray(this.ToArrayFromNextLine());
            }

            #endregion

            #region convert array

            private static int[] ToIntArray(string[] array)
            {
                var result = new int[array.Length];
                for (var i = 0; i < array.Length; i++)
                {
                    result[i] = int.Parse(array[i]);
                }

                return result;
            }

            private static long[] ToLongArray(string[] array)
            {
                var result = new long[array.Length];
                for (var i = 0; i < array.Length; i++)
                {
                    result[i] = long.Parse(array[i]);
                }

                return result;
            }

            private static double[] ToDoubleArray(string[] array)
            {
                var result = new double[array.Length];
                for (var i = 0; i < array.Length; i++)
                {
                    result[i] = double.Parse(array[i]);
                }

                return result;
            }

            #endregion
        }

        #endregion
    }
                    
    #region BinarySearch
    public static class BinarySearch
    {
        /// <summary>
        /// 与えられた比較関数に従って，<paramref name="list"/> の要素のうち，<paramref name="value"/> 以上の要素であるような最小のインデックスを取得します．
        /// </summary>
        /// <typeparam name="T"><see cref="IList{T}"/> の要素の型を指定します．</typeparam>
        /// <param name="list">対象となるコレクション</param>
        /// <param name="value">対象となる要素</param>
        /// <param name="comparison"></param>
        /// <returns><paramref name="value"/> 以上の要素であるような最小の o-indexed でのインデックス．</returns>
        /// <remarks> <paramref name="list"/> は比較関数に対して昇順であることを仮定しています．この関数は O(log N) で実行されます．</remarks>
        public static int LowerBound<T>(this IList<T> list, T value, Comparison<T> comparison) => SearchImpl(list, value, Comparer<T>.Create(comparison), true);

        /// <summary>
        ///　デフォルトの比較関数に従って，<paramref name="list"/> の要素のうち，<paramref name="value"/> 以上の要素であるような最小のインデックスを取得します．
        /// </summary>
        /// <typeparam name="T"><see cref="IList{T}"/> の要素の型を指定します．</typeparam>
        /// <param name="list">対象となるコレクション</param>
        /// <param name="value">対象となる要素</param>
        /// <returns><paramref name="value"/> 以上の要素であるような最小の o-indexed でのインデックス．</returns>
        /// <remarks> <paramref name="list"/> は比較関数に対して昇順であることを仮定しています．この関数は O(log N) で実行されます．</remarks>
        public static int LowerBound<T>(this IList<T> list, T value) => SearchImpl(list, value, Comparer<T>.Default, true);

        /// <summary>
        /// 与えられた比較関数に従って，<paramref name="list"/> の要素のうち，<paramref name="value"/> 以上の要素であるような最小のインデックスを取得します．
        /// </summary>
        /// <typeparam name="T"><see cref="IList{T}"/> の要素の型を指定します．</typeparam>
        /// <param name="list">対象となるコレクション</param>
        /// <param name="value">対象となる要素</param>
        /// <param name="comparison"></param>
        /// <returns><paramref name="value"/> 以上の要素であるような最小の o-indexed でのインデックス．</returns>
        /// <remarks> <paramref name="list"/> は比較関数に対して昇順であることを仮定しています．この関数は O(log N) で実行されます．</remarks>
        public static int UpperBound<T>(this IList<T> list, T value, Comparison<T> comparison) => SearchImpl(list, value, Comparer<T>.Create(comparison), false);

        /// <summary>
        ///　デフォルトの比較関数に従って，<paramref name="list"/> の要素のうち，<paramref name="value"/> より真に大きい要素が現れる最小のインデックスを取得します．
        /// </summary>
        /// <typeparam name="T"><see cref="IList{T}"/> の要素の型を指定します．</typeparam>
        /// <param name="list">対象となるコレクション</param>
        /// <param name="value">対象となる要素</param>
        /// <returns><paramref name="value"/> 以上の要素であるような最小の o-indexed でのインデックス．</returns>
        /// <remarks> <paramref name="list"/> は比較関数に対して昇順であることを仮定しています．この関数は O(log N) で実行されます．</remarks>
        public static int UpperBound<T>(this IList<T> list, T value) => SearchImpl(list, value, Comparer<T>.Default, false);

        private static int SearchImpl<T>(IList<T> list, T value, IComparer<T> comparer, bool isLowerBound)
        {
            var l = 0;
            var r = list.Count - 1;
            while (l <= r)
            {
                var m = (l + r) / 2;
                var res = comparer.Compare(list[m], value);
                if (res < 0 || (res == 0 && !isLowerBound))
                {
                    l = m + 1;
                }
                else
                {
                    r = m - 1;
                }
            }
            return l;
        }
    }
    #endregion
}