#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
 
using namespace std;
using namespace __gnu_pbds;
 
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define fbo find_by_order
#define ook order_of_key
 
typedef long long ll;
typedef pair<int,int> ii;
typedef vector<int> vi;
typedef long double ld; 
typedef tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update> pbds;
typedef set<ll>::iterator sit;
typedef map<ll,ll>::iterator mit;
 
char br[2001][2001];
int b[2001][2001];
int a[2001][2001];
int R,C;
int maxHist(int row[])
{
    // Create an empty stack. The stack holds indexes of
    // hist[] array/ The bars stored in stack are always
    // in increasing order of their heights.
    stack<int> result;
 
    int top_val;     // Top of stack
 
    int max_area = 0; // Initialize max area in current
                      // row (or histogram)
 
    int area = 0;    // Initialize area with current top
 
    // Run through all bars of given histogram (or row)
    int i = 0;
    while (i < C)
    {
        // If this bar is higher than the bar on top stack,
        // push it to stack
        if (result.empty() || row[result.top()] <= row[i])
            result.push(i++);
 
        else
        {
            // If this bar is lower than top of stack, then
            // calculate area of rectangle with stack top as
            // the smallest (or minimum height) bar. 'i' is
            // 'right index' for the top and element before
            // top in stack is 'left index'
            top_val = row[result.top()];
            result.pop();
            area = (top_val+1) * (i+1);
 
            if (!result.empty())
                area = (top_val+1) * (i - result.top());
            max_area = max(area, max_area);
        }
    }
 
    // Now pop the remaining bars from stack and calculate area
    // with every popped bar as the smallest bar
    while (!result.empty())
    {
        top_val = row[result.top()];
        result.pop();
        area = (top_val+1) * (i+1);
        if (!result.empty())
            area = (top_val+1) * (i - result.top() );
 
        max_area = max(area, max_area);
    }
    return max_area;
}
 
// Returns area of the largest rectangle with all 1s in A[][]
int maxRectangle(int A[][2001])
{
    // Calculate area for first row and initialize it as
    // result
    int result = maxHist(A[0]);
 
    // iterate over row to find maximum rectangular area
    // considering each row as histogram
    for (int i = 1; i < R; i++)
    {
 
        for (int j = 0; j < C; j++)
        {
 
            // if A[i][j] is 1 then add A[i -1][j]
            if (A[i][j]) A[i][j] += A[i - 1][j];
 
		}
        // Update result if area with current row (as last row)
        // of rectangle) is more
        result = max(result, maxHist(A[i]));
    }
 
    return result;
}
 
int main()
{
	ios_base::sync_with_stdio(0); cin.tie(0);
	int n,m; cin>>n>>m;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			cin>>br[i][j];
		}
	}
	for(int i=1;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			if(br[i][j]==br[i-1][j])
			{
				b[i][j]=0;
			}
			else
			{
				b[i][j]=1;
			}
		}
	}
	for(int i=1;i<n;i++)
	{
		for(int j=1;j<m;j++)
		{
			if(b[i][j]==b[i][j-1])
			{
				a[i-1][j-1]=1;
			}
			else
			{
				a[i-1][j-1]=0;
			}
		}
	}
	R=n-1;C=m-1;
	cout<<max(n,max(m,maxRectangle(a)))<<'\n';
}


Measured memory_bytes: 35456