#include <bits/stdc++.h>
using namespace std;
#define f first
#define s second
#define int long long
static inline vector<int> manacher_even(const string &s){
int n = (int)s.size();
vector<int> d2(n,0);
int l=0,r=-1;
for(int i=0;i<n;i++){
int k=(i>r)?0:min(d2[l+r-i+1],r-i+1);
while(i-k-1>=0 && i+k<n && s[i-k-1]==s[i+k]) k++;
d2[i]=k;
if(i+k-1>r){ l=i-k; r=i+k-1; }
}
vector<int> even(max(0LL,n-1LL),0);
for(int c=0;c<=n-2;c++) even[c]=d2[c+1];
return even;
}
struct SegTree{
int n; vector<int> st;
SegTree(){}
SegTree(const vector<int>& arr){
n=(int)arr.size(); st.assign(2*n,INT_MAX);
for(int i=0;i<n;i++) st[n+i]=arr[i];
for(int i=n-1;i>0;i--) st[i]=min(st[i<<1],st[i<<1|1]);
}
int rangeMin(int l,int r){
int res=INT_MAX;
for(l+=n,r+=n;l<=r;l>>=1,r>>=1){
if(l&1) res=min(res,st[l++]);
if(!(r&1)) res=min(res,st[r--]);
}
return res;
}
};
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
freopen("origami.in","r",stdin);
freopen("origami.out","w",stdout);
int N,M; cin>>N>>M;
vector<string> A(N);
for(int i=0;i<N;i++) cin>>A[i];
vector<vector<int>> rowEven(N);
for(int i=0;i<N;i++) if(M>=2) rowEven[i]=manacher_even(A[i]);
vector<vector<int>> colEven(M);
if(N>=2){
string col; col.reserve(N);
for(int j=0;j<M;j++){
col.clear();
for(int i=0;i<N;i++) col.push_back(A[i][j]);
colEven[j]=manacher_even(col);
}
}
vector<SegTree> rowSeg(N), colSeg(M);
for(int i=0;i<N;i++) if(M>=2) rowSeg[i]=SegTree(rowEven[i]);
for(int j=0;j<M;j++) if(N>=2) colSeg[j]=SegTree(colEven[j]);
unsigned long long B=(unsigned long long)max(N,M)+1ULL;
auto pack=[&](int r1,int c1,int r2,int c2)->unsigned long long{
return (((unsigned long long)r1*B+(unsigned long long)c1)*B+(unsigned long long)r2)*B+(unsigned long long)c2;
};
unordered_set<unsigned long long> vis;
deque<array<int,4>> dq;
dq.push_back({0,0,N-1,M-1});
vis.insert(pack(0,0,N-1,M-1));
auto push_state=[&](int r1,int c1,int r2,int c2){
if(r1>r2||c1>c2) return;
unsigned long long key=pack(r1,c1,r2,c2);
if(vis.find(key)==vis.end()){
vis.insert(key);
dq.push_back({r1,c1,r2,c2});
}
};
while(!dq.empty()){
auto cur=dq.front();dq.pop_front();
int r1=cur[0],c1=cur[1],r2=cur[2],c2=cur[3];
if(M>=2){
for(int c=c1;c<=c2-1;c++){
int leftSize=c-c1+1,rightSize=c2-c;
int need=min(leftSize,rightSize);
bool ok=true;
for(int i=r1;i<=r2;i++){
if(rowSeg[i].rangeMin(c,c)<need){ ok=false; break; }
}
if(!ok) continue;
if(leftSize<=rightSize) push_state(r1,c+1,r2,c2);
if(rightSize<=leftSize) push_state(r1,c1,r2,c);
}
}
if(N>=2){
for(int r=r1;r<=r2-1;r++){
int topSize=r-r1+1,botSize=r2-r;
int need=min(topSize,botSize);
bool ok=true;
for(int j=c1;j<=c2;j++){
if(colSeg[j].rangeMin(r,r)<need){ ok=false; break; }
}
if(!ok) continue;
if(topSize<=botSize) push_state(r+1,c1,r2,c2);
if(botSize<=topSize) push_state(r1,c1,r,c2);
}
}
}
cout<<(int)vis.size()<<'\n';
return 0;
}