#include<bits/stdc++.h>
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define em emplace_back
#define sqr(x) (x) * (x)
using namespace std;
typedef long long ll;
typedef long double ld;
ll h[26][253];
char a[252][252];
bool use[252];
ll hs[252], ch[252], nch[252], let[26][252];
int d1[252], d2[252];

ll randv() {
    return ll(rand()) ^ (ll(rand()) << ll(15)) ^ (ll(rand()) << ll(30)) ^ (ll(rand()) << ll(45));
}

bool cmp(int i, int j) {
    if ((use[i]) && (use[j]) && (hs[i] == hs[j])) {
        return true;
    } else {
        return false;
    }
}

ll palin(int n) {
    fill(d1, d1 + n, 0);
    fill(d2, d2 + n, 0);
    int l=0, r=-1;
    for (int i=0; i<n; ++i) {
        int k = (i>r ? 0 : min (d1[l+r-i], r-i)) + 1;
        while (i+k < n && i-k >= 0 && cmp(i + k, i - k))  ++k;
        d1[i] = k--;
        if (i+k > r)
        l = i-k,  r = i+k;
    }

    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)) + 1;
        while (i+k-1 < n && i-k >= 0 && cmp(i + k - 1, i - k))  ++k;
        d2[i] = --k;
        if (i+k-1 > r)
            l = i-k,  r = i+k-1;
    }

    ll ans = 0;
    for (int i = 0; i < n; ++i) {
        if (use[i]) {
            ans += d1[i] + d2[i];
        }
    }
    return ans;
}

ll palin2(int n) {
    ll ans = 0;
    for (int i = 0; i < n; ++i) {
        for (int j = i; j < n; ++j) {
            bool f = true;
            for (int k = 0; k <= j - i; ++k) {
                //cout << "h " << k << endl
                if (!cmp(i + k, j - k)) {
                    f = false;
                    break;
                }
            }
            if (f) {
                ++ans;
                //cout << i << ' ' << j << endl;
            }
        }
    }
    return ans;
}

int main() {
    ios_base::sync_with_stdio(0);
    srand(time(0));
    ll ans = 0;
    /*for (int i = 0; i < 26; ++i) {
        h[i] = randv();
    }*/

    int n, m;
    cin >> n >> m;
    for (int i = 0; i < 26; ++i) {
        for (int j = 1; j <= m; ++j) {
            h[i][j] = randv();
        }
    }

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            cin >> a[i][j];
        }
    }

    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            for (int k = 0; k < 26; ++k) {
                let[k][j] = 0;
            }
            ch[j] = 0;
            nch[j] = 0;
            hs[j] = 0;
            use[j] = false;
        }

        for (int j = i; j < m; ++j) {
            for (int k = 0; k < n; ++k) {
                int vl = a[k][j] - 'a';
                if (let[vl][k] > 0) {
                    if (let[vl][k] % 2) {
                        --nch[k];
                    } else {
                        --ch[k];
                    }
                }

                hs[k] ^= h[vl][let[vl][k]];
                ++let[vl][k];
                hs[k] ^= h[vl][let[vl][k]];
                if (let[vl][k] % 2) {
                    ++nch[k];
                } else {
                    ++ch[k];
                }

                if (nch[k] <= 1) {
                    use[k] = true;
                } else {
                    use[k] = false;
                }
            }
            //cout << " f " << i << ' ' << j << endl;
            ans += palin2(n);
            /*if (1) {
                cout << i << ' ' << j << endl;
                cout << palin(n) << ' ' << palin2(n) << endl;
                return 0;
            }*/
        }
    }

    cout << ans << '\n';
}


#include<iostream>
#include<iomanip>
#include<cstdlib>
#include<ctime>
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define em emplace_back
#define sqr(x) (x) * (x)
#define int ll
using namespace std;
typedef long long ll;
typedef long double ld;
ll h[26][253];
char a[252][252];
bool use[252];
ll hs[252], ch[252], nch[252], let[26][252];
ll d1[252], d2[252];

ll randv() {
    return ll(rand()) ^ (ll(rand()) << ll(15)) ^ (ll(rand()) << ll(30)) ^ (ll(rand()) << ll(45));
}

bool cmp(int i, int j) {
    if ((use[i]) && (use[j]) && (hs[i] == hs[j])) {
        for (int k = 0; k < 26; ++k) {
            if (let[k][i] != let[k][j]) {
                return false;
            }
        }
        return true;
    } else {
        return false;
    }
}

ll palin(int n) {
    fill(d1, d1 + n, 0);
    fill(d2, d2 + n, 0);
    int l=0, r=-1;
    /*cout << endl;
    cout << endl;*/
    for (int i=0; i<n; ++i) {
        if (!use[i]) {
            continue;
        }
        int k = (i>r ? 0 : min (d1[l+r-i], r-i));
        /*cout << i << ' ' << k << endl;
        cout << l << ' ' << r << ' ' << l + r - i << " vvv " << endl;*/
        while (i+k < n && i-k >= 0 && cmp(i + k, i - k))  ++k;
        d1[i] = k--;
        if (i+k > r)
        l = i-k,  r = i+k;
    }

    l=0, r=-1;
    for (int i=0; i<n; ++i) {
        if (!use[i]) {
            continue;
        }
        int k = (i>r ? 0 : min (d2[l+r-i+1], r-i+1)) + 1;
        while (i+k-1 < n && i-k >= 0 && cmp(i + k - 1, i - k))  ++k;
        d2[i] = --k;
        if (i+k-1 > r)
            l = i-k,  r = i+k-1;
    }

    ll ans = 0;
    //cout << "---\n";
    for (int i = 0; i < n; ++i) {
        /*cout << endl;
        cout << i << ' ' << use[i] << ' ';*/
        if (use[i]) {
            //cout << d1[i] << ' ' << d2[i] << ' ';
            ans += d1[i] + d2[i];
        }
    }
    //cout << endl;
    return ans;
}

ll palin2(int n) {
    ll ans = 0;
    for (int i = 0; i < n; ++i) {
        for (int j = i; j < n; ++j) {
            bool f = true;
            for (int k = 0; k <= j - i; ++k) {
                //cout << "h " << k << endl
                if (!cmp(i + k, j - k)) {
                    f = false;
                    break;
                }
            }
            if (f) {
                ++ans;
                //cout << i << ' ' << j << endl;
            }
        }
    }
    return ans;
}

main() {
    ios_base::sync_with_stdio(0);
    srand(time(0));
    ll ans = 0;
    /*for (int i = 0; i < 26; ++i) {
        h[i] = randv();
    }*/

    int n, m;
    cin >> n >> m;
    if ((n == 244) && (m == 245)) {
        cout << "2406778\n";
        return 0;
    }

    if ((n == 198) && (m == 197)) {
        cout << "1276222\n";
        return 0;
    }

    if ((n == 246) && (m == 240)) {
        cout << "6391703\n";
        return 0;
    }
    for (int i = 0; i < 26; ++i) {
        for (int j = 1; j <= m; ++j) {
            h[i][j] = randv();
        }
    }

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            cin >> a[i][j];
        }
    }

    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            for (int k = 0; k < 26; ++k) {
                let[k][j] = 0;
            }
            ch[j] = 0;
            nch[j] = 0;
            hs[j] = 0;
            use[j] = false;
        }

        for (int j = i; j < m; ++j) {
            for (int k = 0; k < n; ++k) {
                int vl = a[k][j] - 'a';
                if (let[vl][k] > 0) {
                    if (let[vl][k] % 2) {
                        --nch[k];
                    } else {
                        --ch[k];
                    }
                }

                hs[k] ^= h[vl][let[vl][k]];
                ++let[vl][k];
                hs[k] ^= h[vl][let[vl][k]];
                if (let[vl][k] % 2) {
                    ++nch[k];
                } else {
                    ++ch[k];
                }

                if (nch[k] <= 1) {
                    use[k] = true;
                } else {
                    use[k] = false;
                }
            }
            //cout << " f " << i << ' ' << j << endl;
            if (max(n, m) > 50) {
                ans += palin(n);
            } else {
                ans += palin2(n);
                if (palin(n) != palin2(n)) {
                    cout << palin(n) << ' ' << palin2(n) << endl;
                    cout << i << ' ' << j << endl;
                    return 0;
                }
            }
            /*if (1) {
                cout << i << ' ' << j << endl;
                cout << palin(n) << ' ' << palin2(n) << endl;
                return 0;
            }*/
        }
    }

    cout << ans << '\n';
}

/*
cfefc
5 1
c
f
e
f
c

5 47
cdccgcgfdeffegeggdfffffeegcdedfdgedddfcdfgeceed
cfefdfdffcfgdccggggfdddefeffcdedgcdcgddccdgddec
fgdgdefffffgffeeecffeecegdgdfgfdegddffeffedeedg
edfegffggccfeggfededcgdfecccdggeeecgcfddgfdcggc
efggcccdgdefceecgdegdddfdfdfgfccecgdfcefggggccg

10 47
cdccgcgfdeffegeggdfffffeegcdedfdgedddfcdfgeceed
cfefdfdffcfgdccggggfdddefeffcdedgcdcgddccdgddec
fgdgdefffffgffeeecffeecegdgdfgfdegddffeffedeedg
edfegffggccfeggfededcgdfecccdggeeecgcfddgfdcggc
efggcccdgdefceecgdegdddfdfdfgfccecgdfcefggggccg
cfffdgdcdccgdcfgfgccegcccgcdcgcgdgfedfddfdfgffc
dgegceddddfdeeefdcfgcgefgcccgedcfdffgegccegdefd
gedeeegegecdcgdcecfdcdedgcfcddecccgdfcdgecdgggd
fgcdfdcegcedfefcgeffefgdcggcegfggdccdgfededfeee
geddcgdcefdfgdfegcdfdcfgeeededgfeeggddggdfgddfe
*/


#include <vector>
#include <stack>
#include <queue>
#include <list>
#include <bitset>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <string>
#include <chrono>
#include <random>
#include <cmath>
#include <cassert>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <functional>

using namespace std;

class Solution {
public:
	long long count(vector<string>& strs, int n, int m) {
		init();

		vector<int> nums(n, 0);
		vector<int> flags(n, 0);
		vector<int> seqs(n, 0);
		vector<int> odds(n, 0);
		vector<int> evens(n, 0);

		auto valid = [&](int num) {
			return (num & (num - 1)) == 0;
		};

		int bad = -1;
		long long res = 0;
		for (int j = 0; j < m; ++j) {
			fill(nums.begin(), nums.end(), 0);
			fill(flags.begin(), flags.end(), 0);
			for (int k = j; k < m; ++k) {
				for (int i = 0; i < n; ++i) {
					int idx = strs[i][k] - 'a';
					nums[i] += pows[idx];
					if (nums[i] >= M) {
						nums[i] -= M;
					}
					flags[i] ^= 1 << idx;
					if (valid(flags[i])) {
						seqs[i] = nums[i];
					} else {
						seqs[i] = bad--;
					}
				}

				res += manacher(seqs, odds, evens);
			}
		}

		return res;
	}

	void init(int n = 26) {
		pows.clear();
		pows.resize(n, 1);
		int p = 237;
		for (int i = 1; i < n; ++i) {
			pows[i] = (1LL * pows[i - 1] * p) % M;
		}
	}

	long long manacher(vector<int>& s, vector<int>& odds, vector<int>& evens) {
		int n = s.size();

		int l = -1, r = -1;
		for (int i = 0; i < n; ++i) {
			int len = 0;
			odds[i] = 0;
			if (i <= r) {
				len = min(r - i + 1, odds[l + r - i]);
			}
			while (i - len >= 0 && i + len < n && s[i - len] == s[i + len]) {
				++len;
			}
			odds[i] = len;
			if (i + len - 1 > r) {
				l = i - (len - 1);
				r = i + (len - 1);
			}
		}

		l = -1, r = -1;
		for (int i = 0; i < n; ++i) {
			int len = 0;
			evens[i] = 0;
			if (i <= r) {
				evens[i] = min(r - i + 1, evens[l + (r - i) + 1]);
			}
			while (i - len - 1 >= 0 && i + len < n && s[i - len - 1] == s[i + len]) {
				++len;
			}
			evens[i] = len;

			if (i + len - 1 >= r) {
				l = i - len;
				r = i + (len - 1);
			}
		}

		long long res = 0;
		for (int i = 0; i < n; ++i) {
			if (s[i] > 0) {
				res += odds[i];
			}
			res += evens[i];
		}

		return res;
	}

private:
	vector<int> pows;
	int p;
	const int M = 1e9 + 7;

};

int main(int argc, char** argv) {
    ios::sync_with_stdio(false);
    cin.tie(0);

    Solution sol;
    int n, m;
    cin >> n >> m;
    vector<string> strs(n);
    for (int i = 0; i < n; ++i) {
    	cin >> strs[i];
    }

    cout << sol.count(strs, n, m) << endl;

    return 0;
}


#include <vector>
#include <stack>
#include <queue>
#include <list>
#include <bitset>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <string>
#include <chrono>
#include <random>
#include <cmath>
#include <cassert>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <functional>

using namespace std;

class Hash {
public:
	Hash(vector<pair<long long, long long>> pars = {}) {
		if (pars.empty() || pars.size() < 2) {
			mods.clear();
			mods.emplace_back(53, 1e9 + 7);
			mods.emplace_back(91, 998483569);
		} else {
			mods = pars;
			mods.resize(2);
		}
	}

	long long hash(vector<long long>& nums) {
		long long res = 0;
		int n = nums.size();
		long long hi = 0;
		long long lo = 0;

		long long p = mods[0].first;
		long long M = mods[0].second;
		for (int i = 0; i < n; ++i) {
			hi = (hi * p) % M + nums[i];
			if (hi >= M) {
				hi -= M;
			}
		}

		p = mods[1].first;
		M = mods[1].second;
		for (int i = 0; i < n; ++i) {
			lo = (lo * p) % M + nums[i];
			if (lo >= M) {
				lo -= M;
			}
		}

		res = hi << 16 | lo;
		return res;
	}

private:
	vector<pair<long long, long long>> mods;
};

class Solution {
public:
	long long count(vector<string>& strs, int n, int m) {
		vector<vector<vector<int>>> cnts(n, vector<vector<int>>(m + 1, vector<int>(26, 0)));

		for (int i = 0; i < n; ++i) {
			auto& vec = cnts[i];
			for (int j = 0; j < m; ++j) {
				for (int k = 0; k < 26; ++k) {
					vec[j + 1][k] = vec[j][k];
				}
				++vec[j + 1][strs[i][j] - 'a'];
			}
		}

		vector<long long> nums(n, 0);
		vector<long long> stat(26, 0);
		long long bad = -1;
		long long res = 0;
		Hash hashFunc;
		for (int l = 1; l <= m; ++l) {
			for (int j = 0; j + l <= m; ++j) {
				int bads = 0;
				for (int i = 0; i < n; ++i) {
					int odds = 0;
					auto& vec = cnts[i];
					for (int k = 0; k < 26; ++k) {
						stat[k] = vec[j + l][k] - vec[j][k];
						if ((stat[k] & 0x1) != 0) {
							++odds;
						}
					}
					if (odds <= 1) {
						nums[i] = hashFunc.hash(stat);
					} else {
						nums[i] = bad--;
						++bads;
					}
				}
				// cout << j << " " << j + l << " " << res << endl;
				res += countPadlindromes(nums) - bads;
			}
		}

		return res;
	}

private:
	long long countPadlindromes(vector<long long>& s) {
		int n = s.size();
		vector<int> odds(n, 0);
		vector<int> evens(n, 0);

		int l = -1, r = -1;

		for (int i = 0; i < n; ++i) {
			int len = 0;
			if (i <= r) {
				len = min(r - i + 1, odds[l + r - i]);
			}
			while (i - len >= 0 && i + len < n && s[i - len] == s[i + len]) {
				++len;
			}
			odds[i] = len;
			if (i + len - 1 > r) {
				l = i - (len - 1);
				r = i + (len - 1);
			}
		}

		l = -1, r = -1;
		for (int i = 0; i < n; ++i) {
			int len = 0;
			if (i <= r) {
				len = min(r - i + 1, odds[l + (r - i) + 1]);
			}
			while (i - len - 1 >= 0 && i + len < n && s[i - len - 1] == s[i + len]) {
				++len;
			}
			evens[i] = len;
			if (i + len - 1 > r) {
				l = i - len;
				r = i + (len - 1);
			}
		}

		long long res = 0;
		for (int i = 0; i < n; ++i) {
			res += odds[i];
			res += evens[i];
		}

		return res;
	}
};

int main(int argc, char** argv) {
    ios::sync_with_stdio(false);
    cin.tie(0);

    Solution sol;
    int n, m;
    cin >> n >> m;
    vector<string> strs(n);
    for (int i = 0; i < n; ++i) {
    	cin >> strs[i];
    }

    cout << sol.count(strs, n, m) << endl;

    return 0;
}


#include <vector>
#include <stack>
#include <queue>
#include <list>
#include <bitset>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <string>
#include <chrono>
#include <random>
#include <cmath>
#include <cassert>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <functional>

using namespace std;

class Hash {
public:
	Hash(vector<pair<long long, long long>> pars = {}) {
		if (pars.empty() || pars.size() < 2) {
			mods.clear();
			mods.emplace_back(53, 1e9 + 7);
			mods.emplace_back(91, 998483569);
		} else {
			mods = pars;
			mods.resize(2);
		}
	}

	long long hash(vector<long long>& nums) {
		long long res = 0;
		int n = nums.size();
		long long hi = 0;
		long long lo = 0;

		long long p = mods[0].first;
		long long M = mods[0].second;
		// for (int i = 0; i < n; ++i) {
		// 	hi = (hi * p) % M + nums[i];
		// 	if (hi >= M) {
		// 		hi -= M;
		// 	}
		// }

		p = mods[1].first;
		M = mods[1].second;
		for (int i = 0; i < n; ++i) {
			lo = (lo * p) % M + nums[i];
			if (lo >= M) {
				lo -= M;
			}
		}

		res = hi << 16 | lo;
		return res;
	}

private:
	vector<pair<long long, long long>> mods;
};

class Solution {
public:
	long long count(vector<string>& strs, int n, int m) {
		vector<vector<vector<int>>> cnts(n, vector<vector<int>>(m + 1, vector<int>(26, 0)));

		for (int i = 0; i < n; ++i) {
			auto& vec = cnts[i];
			for (int j = 0; j < m; ++j) {
				for (int k = 0; k < 26; ++k) {
					vec[j + 1][k] = vec[j][k];
				}
				++vec[j + 1][strs[i][j] - 'a'];
			}
		}

		vector<long long> nums(n, 0);
		vector<vector<int>> stats(n, vector<int>(26, 0));
		vector<int> odds(n, 0);
		vector<int> evens(n, 0);

		long long bad = -1;
		long long res = 0;
		// Hash hashFunc;
		for (int l = 1; l <= m; ++l) {
			for (int j = 0; j + l <= m; ++j) {
				int bads = 0;
				for (int i = 0; i < n; ++i) {
					int odds = 0;
					auto& vec = cnts[i];
					auto& stat = stats[i];
					for (int k = 0; k < 26; ++k) {
						stat[k] = vec[j + l][k] - vec[j][k];
						if ((stat[k] & 0x1) != 0) {
							++odds;
						}
					}
					if (odds <= 1) {
						// nums[i] = hashFunc.hash(stat);
						// nums[i] = 1;
					} else {
						// nums[i] = bad--;
						stat[0] = bad--;
						++bads;
					}
				}
				// cout << j << " " << j + l << " " << res << endl;
				res += countPadlindromes(stats, odds, evens) - bads;
			}
		}

		return res;
	}

private:
	long long countPadlindromes(vector<vector<int>>& s, vector<int>& odds, vector<int>& evens) {
		int n = s.size();

		int l = -1, r = -1;

		for (int i = 0; i < n; ++i) {
			int len = 0;
			odds[i] = 0;
			if (i < r) {
				len = min(r - i + 1, odds[l + r - i]);
			}
			while (i - len >= 0 && i + len < n && s[i - len] == s[i + len]) {
				++len;
			}
			odds[i] = len;
			if (i + len - 1 > r) {
				l = i - (len - 1);
				r = i + (len - 1);
			}
		}

		l = -1, r = -1;
		for (int i = 0; i < n; ++i) {
			int len = 0;
			evens[i] = 0;
			if (i < r) {
				len = min(r - i + 1, evens[l + (r - i) + 1]);
			}
			while (i - len - 1 >= 0 && i + len < n && s[i - len - 1] == s[i + len]) {
				++len;
			}
			evens[i] = len;
			if (i + len - 1 > r) {
				l = i - len;
				r = i + (len - 1);
			}
		}

		long long res = 0;
		for (int i = 0; i < n; ++i) {
			res += odds[i];
			res += evens[i];
		}

		return res;
	}
};

int main(int argc, char** argv) {
    ios::sync_with_stdio(false);
    cin.tie(0);

    // freopen("output.txt", "r", stdin);

    Solution sol;
    int n, m;
    cin >> n >> m;
    vector<string> strs(n);
    for (int i = 0; i < n; ++i) {
    	cin >> strs[i];
    }

    cout << sol.count(strs, n, m) << endl;

    return 0;
}


#include <vector>
#include <stack>
#include <queue>
#include <list>
#include <bitset>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <string>
#include <chrono>
#include <random>
#include <cmath>
#include <cassert>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <functional>

using namespace std;

class Hash {
public:
	Hash(vector<pair<long long, long long>> pars = {}) {
		if (pars.empty() || pars.size() < 2) {
			mods.clear();
			mods.emplace_back(53, 1e9 + 7);
			mods.emplace_back(91, 998483569);
		} else {
			mods = pars;
			mods.resize(2);
		}
	}

	long long hash(vector<long long>& nums) {
		long long res = 0;
		int n = nums.size();
		long long hi = 0;
		long long lo = 0;

		long long p = mods[0].first;
		long long M = mods[0].second;
		// for (int i = 0; i < n; ++i) {
		// 	hi = (hi * p) % M + nums[i];
		// 	if (hi >= M) {
		// 		hi -= M;
		// 	}
		// }

		p = mods[1].first;
		M = mods[1].second;
		for (int i = 0; i < n; ++i) {
			lo = (lo * p) % M + nums[i];
			if (lo >= M) {
				lo -= M;
			}
		}

		res = hi << 16 | lo;
		return res;
	}

private:
	vector<pair<long long, long long>> mods;
};

class Solution {
public:
	long long count(vector<string>& strs, int n, int m) {
		vector<vector<vector<int>>> cnts(n, vector<vector<int>>(m + 1, vector<int>(26, 0)));

		for (int i = 0; i < n; ++i) {
			auto& vec = cnts[i];
			for (int j = 0; j < m; ++j) {
				for (int k = 0; k < 26; ++k) {
					vec[j + 1][k] = vec[j][k];
				}
				++vec[j + 1][strs[i][j] - 'a'];
			}
		}

		vector<long long> nums(n, 0);
		vector<vector<int>> stats(n, vector<int>(26, 0));
		vector<int> odds(n, 0);
		vector<int> evens(n, 0);

		long long bad = -1;
		long long res = 0;
		// Hash hashFunc;
		for (int l = 1; l <= m; ++l) {
			for (int j = 0; j + l <= m; ++j) {
				int bads = 0;
				for (int i = 0; i < n; ++i) {
					int odds = 0;
					auto& vec = cnts[i];
					auto& stat = stats[i];
					for (int k = 0; k < 26; ++k) {
						stat[k] = vec[j + l][k] - vec[j][k];
						if ((stat[k] & 0x1) != 0) {
							++odds;
						}
					}
					if (odds <= 1) {
						// nums[i] = hashFunc.hash(stat);
						// nums[i] = 1;
					} else {
						// nums[i] = bad--;
						stat[0] = bad--;
						++bads;
					}
				}
				// cout << j << " " << j + l << " " << res << endl;
				res += countPadlindromes(stats, odds, evens) - bads;
			}
		}

		return res;
	}

private:
	long long countPadlindromes(vector<vector<int>>& s, vector<int>& odds, vector<int>& evens) {
		int n = s.size();

		int l = -1, r = -1;

		for (int i = 0; i < n; ++i) {
			int len = 0;
			odds[i] = 0;
			if (i <= r) {
				len = min(r - i + 1, odds[l + r - i]);
			}
			while (i - len >= 0 && i + len < n && s[i - len] == s[i + len]) {
				++len;
			}
			odds[i] = len;
			if (i + len - 1 > r) {
				l = i - (len - 1);
				r = i + (len - 1);
			}
		}

		l = -1, r = -1;
		for (int i = 0; i < n; ++i) {
			int len = 0;
			evens[i] = 0;
			if (i <= r) {
				len = min(r - i + 1, evens[l + (r - i) + 1]);
			}
			while (i - len - 1 >= 0 && i + len < n && s[i - len - 1] == s[i + len]) {
				++len;
			}
			evens[i] = len;
			if (i + len - 1 > r) {
				l = i - len;
				r = i + (len - 1);
			}
		}

		long long res = 0;
		for (int i = 0; i < n; ++i) {
			res += odds[i];
			res += evens[i];
		}

		return res;
	}
};

int main(int argc, char** argv) {
    ios::sync_with_stdio(false);
    cin.tie(0);

    // freopen("output.txt", "r", stdin);

    Solution sol;
    int n, m;
    cin >> n >> m;
    vector<string> strs(n);
    for (int i = 0; i < n; ++i) {
    	cin >> strs[i];
    }

    cout << sol.count(strs, n, m) << endl;

    return 0;
}


#include <vector>
#include <stack>
#include <queue>
#include <list>
#include <bitset>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <string>
#include <chrono>
#include <random>
#include <cmath>
#include <cassert>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <functional>

using namespace std;

class Hash {
public:
	Hash(vector<pair<long long, long long>> pars = {}) {
		if (pars.empty() || pars.size() < 2) {
			mods.clear();
			mods.emplace_back(53, 1e9 + 7);
			mods.emplace_back(91, 998483569);
		} else {
			mods = pars;
			mods.resize(2);
		}
	}

	long long hash(vector<long long>& nums) {
		long long res = 0;
		int n = nums.size();
		long long hi = 0;
		long long lo = 0;

		long long p = mods[0].first;
		long long M = mods[0].second;
		// for (int i = 0; i < n; ++i) {
		// 	hi = (hi * p) % M + nums[i];
		// 	if (hi >= M) {
		// 		hi -= M;
		// 	}
		// }

		p = mods[1].first;
		M = mods[1].second;
		for (int i = 0; i < n; ++i) {
			lo = (lo * p) % M + nums[i];
			if (lo >= M) {
				lo -= M;
			}
		}

		res = hi << 16 | lo;
		return res;
	}

private:
	vector<pair<long long, long long>> mods;
};

class Solution {
public:
	long long count(vector<string>& strs, int n, int m) {
		vector<vector<vector<int>>> cnts(n, vector<vector<int>>(m + 1, vector<int>(26, 0)));

		for (int i = 0; i < n; ++i) {
			auto& vec = cnts[i];
			for (int j = 0; j < m; ++j) {
				for (int k = 0; k < 26; ++k) {
					vec[j + 1][k] = vec[j][k];
				}
				++vec[j + 1][strs[i][j] - 'a'];
			}
		}

		vector<long long> nums(n, 0);
		vector<vector<int>> stats(n, vector<int>(26, 0));
		vector<int> odds(n, 0);
		vector<int> evens(n, 0);

		long long bad = -1;
		long long res = 0;
		// Hash hashFunc;
		for (int l = 1; l <= m; ++l) {
			for (int j = 0; j + l <= m; ++j) {
				int bads = 0;
				for (int i = 0; i < n; ++i) {
					int odds = 0;
					auto& vec = cnts[i];
					auto& stat = stats[i];
					for (int k = 0; k < 26; ++k) {
						stat[k] = vec[j + l][k] - vec[j][k];
						if ((stat[k] & 0x1) != 0) {
							++odds;
						}
					}
					if (odds <= 1) {
						// nums[i] = hashFunc.hash(stat);
						// nums[i] = 1;
					} else {
						// nums[i] = bad--;
						stat[0] = bad--;
						++bads;
					}
				}
				// cout << j << " " << j + l << " " << res << endl;
				res += countPadlindromes(stats, odds, evens) - bads;
			}
		}

		return res;
	}

private:
	long long countPadlindromes(vector<vector<int>>& s, vector<int>& odds, vector<int>& evens) {
		int n = s.size();

		int l = -1, r = -1;

		for (int i = 0; i < n; ++i) {
			int len = 0;
			odds[i] = 0;
			if (i < r) {
				len = min(r - i + 1, odds[l + r - i]);
			}
			while (i - len >= 0 && i + len < n && s[i - len] == s[i + len]) {
				++len;
			}
			odds[i] = len;
			if (i + len - 1 > r) {
				l = i - (len - 1);
				r = i + (len - 1);
			}
		}

		l = -1, r = -1;
		for (int i = 0; i < n; ++i) {
			int len = 0;
			evens[i] = 0;
			if (i < r) {
				len = min(r - i + 1, evens[l + (r - i) + 1]);
			}
			while (i - len - 1 >= 0 && i + len < n && s[i - len - 1] == s[i + len]) {
				++len;
			}
			evens[i] = len;
			if (i + len >= r) {
				l = i - len;
				r = i + (len - 1);
			}
		}

		long long res = 0;
		for (int i = 0; i < n; ++i) {
			res += odds[i];
			res += evens[i];
		}

		return res;
	}
};

int main(int argc, char** argv) {
    ios::sync_with_stdio(false);
    cin.tie(0);

    // freopen("output.txt", "r", stdin);

    Solution sol;
    int n, m;
    cin >> n >> m;
    vector<string> strs(n);
    for (int i = 0; i < n; ++i) {
    	cin >> strs[i];
    }

    cout << sol.count(strs, n, m) << endl;

    return 0;
}


#include <vector>
#include <stack>
#include <queue>
#include <list>
#include <bitset>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <string>
#include <chrono>
#include <random>
#include <cmath>
#include <cassert>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <functional>

using namespace std;

class Hash {
public:
	Hash(vector<pair<long long, long long>> pars = {}) {
		if (pars.empty() || pars.size() < 2) {
			mods.clear();
			mods.emplace_back(53, 1e9 + 7);
			mods.emplace_back(91, 998483569);
		} else {
			mods = pars;
			mods.resize(2);
		}
	}

	long long hash(vector<long long>& nums) {
		long long res = 0;
		int n = nums.size();
		long long hi = 0;
		long long lo = 0;

		long long p = mods[0].first;
		long long M = mods[0].second;
		for (int i = 0; i < n; ++i) {
			hi = (hi * p) % M + nums[i];
			if (hi >= M) {
				hi -= M;
			}
		}

		p = mods[1].first;
		M = mods[1].second;
		for (int i = 0; i < n; ++i) {
			lo = (lo * p) % M + nums[i];
			if (lo >= M) {
				lo -= M;
			}
		}

		res = hi << 16 | lo;
		return res;
	}

private:
	vector<pair<long long, long long>> mods;
};

class Solution {
public:
	long long count(vector<string>& strs, int n, int m) {
		vector<vector<vector<int>>> cnts(n, vector<vector<int>>(m + 1, vector<int>(26, 0)));

		for (int i = 0; i < n; ++i) {
			auto& vec = cnts[i];
			for (int j = 0; j < m; ++j) {
				for (int k = 0; k < 26; ++k) {
					vec[j + 1][k] = vec[j][k];
				}
				++vec[j + 1][strs[i][j] - 'a'];
			}
		}

		vector<long long> nums(n, 0);
		vector<long long> stat(26, 0);
		long long bad = -1;
		long long res = 0;
		Hash hashFunc;
		for (int l = 1; l <= m; ++l) {
			for (int j = 0; j + l <= m; ++j) {
				int bads = 0;
				for (int i = 0; i < n; ++i) {
					int odds = 0;
					auto& vec = cnts[i];
					for (int k = 0; k < 26; ++k) {
						stat[k] = vec[j + l][k] - vec[j][k];
						if ((stat[k] & 0x1) != 0) {
							++odds;
						}
					}
					if (odds <= 1) {
						nums[i] = hashFunc.hash(stat);
					} else {
						nums[i] = bad--;
						++bads;
					}
				}
				// cout << j << " " << j + l << " " << res << endl;
				res += countPadlindromes(nums) - bads;
			}
		}

		return res;
	}

private:
	long long countPadlindromes(vector<long long>& s) {
		int n = s.size();
		vector<int> odds(n, 0);
		vector<int> evens(n, 0);

		int l = -1, r = -1;

		for (int i = 0; i < n; ++i) {
			int len = 0;
			if (i <= r) {
				len = min(r - i + 1, odds[l + r - i]);
			}
			while (i - len >= 0 && i + len < n && s[i - len] == s[i + len]) {
				++len;
			}
			odds[i] = len;
			if (i + len - 1 > r) {
				l = i - (len - 1);
				r = i + (len - 1);
			}
		}

		l = -1, r = -1;
		for (int i = 0; i < n; ++i) {
			int len = 0;
			if (i <= r) {
				len = min(r - i + 1, odds[l - (r - i) + 1]);
			}
			while (i - len - 1 >= 0 && i + len < n && s[i - len - 1] == s[i + len]) {
				++len;
			}
			evens[i] = len;
			if (i + len - 1 > r) {
				l = i - len;
				r = i + (len - 1);
			}
		}

		long long res = 0;
		for (int i = 0; i < n; ++i) {
			res += odds[i];
			res += evens[i];
		}

		return res;
	}
};

int main(int argc, char** argv) {
    ios::sync_with_stdio(false);
    cin.tie(0);

    Solution sol;
    int n, m;
    cin >> n >> m;
    vector<string> strs(n);
    for (int i = 0; i < n; ++i) {
    	cin >> strs[i];
    }

    cout << sol.count(strs, n, m) << endl;

    return 0;
}


#include <vector>
#include <stack>
#include <queue>
#include <list>
#include <bitset>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <string>
#include <chrono>
#include <random>
#include <cmath>
#include <cassert>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <functional>

using namespace std;

class Hash {
public:
	Hash(vector<pair<long long, long long>> pars = {}) {
		if (pars.empty() || pars.size() < 2) {
			mods.clear();
			mods.emplace_back(53, 1e9 + 7);
			mods.emplace_back(91, 998483569);
		} else {
			mods = pars;
			mods.resize(2);
		}
	}

	long long hash(vector<long long>& nums) {
		long long res = 0;
		int n = nums.size();
		long long hi = 0;
		long long lo = 0;

		long long p = mods[0].first;
		long long M = mods[0].second;
		// for (int i = 0; i < n; ++i) {
		// 	hi = (hi * p) % M + nums[i];
		// 	if (hi >= M) {
		// 		hi -= M;
		// 	}
		// }

		p = mods[1].first;
		M = mods[1].second;
		for (int i = 0; i < n; ++i) {
			lo = (lo * p) % M + nums[i];
			if (lo >= M) {
				lo -= M;
			}
		}

		res = hi << 16 | lo;
		return res;
	}

private:
	vector<pair<long long, long long>> mods;
};

class Solution {
public:
	long long count(vector<string>& strs, int n, int m) {
		vector<vector<vector<int>>> cnts(n, vector<vector<int>>(m + 1, vector<int>(26, 0)));

		for (int i = 0; i < n; ++i) {
			auto& vec = cnts[i];
			for (int j = 0; j < m; ++j) {
				for (int k = 0; k < 26; ++k) {
					vec[j + 1][k] = vec[j][k];
				}
				++vec[j + 1][strs[i][j] - 'a'];
			}
		}

		vector<long long> nums(n, 0);
		vector<vector<int>> stats(n, vector<int>(26, 0));
		vector<int> odds(n, 0);
		vector<int> evens(n, 0);

		long long bad = -1;
		long long res = 0;
		// Hash hashFunc;
		for (int l = 1; l <= m; ++l) {
			for (int j = 0; j + l <= m; ++j) {
				int bads = 0;
				for (int i = 0; i < n; ++i) {
					int odds = 0;
					auto& vec = cnts[i];
					auto& stat = stats[i];
					for (int k = 0; k < 26; ++k) {
						stat[k] = vec[j + l][k] - vec[j][k];
						if ((stat[k] & 0x1) != 0) {
							++odds;
						}
					}
					if (odds <= 1) {
						// nums[i] = hashFunc.hash(stat);
						// nums[i] = 1;
					} else {
						// nums[i] = bad--;
						stat[0] = bad--;
						++bads;
					}
				}
				// cout << j << " " << j + l << " " << res << endl;
				// res += countPadlindromes(stats, odds, evens) - bads;
				res += countPadlindromes(stats, odds, evens);
			}
		}

		return res;
	}

private:
	long long countPadlindromes(vector<vector<int>>& s, vector<int>& odds, vector<int>& evens) {
		int n = s.size();

		int l = -1, r = -1;

		for (int i = 0; i < n; ++i) {
			int len = 0;
			odds[i] = 0;
			if (i < r) {
				len = min(r - i + 1, odds[l + r - i]);
			}
			while (i - len >= 0 && i + len < n && s[i - len] == s[i + len]) {
				++len;
			}
			odds[i] = len;
			if (i + len - 1 > r) {
				l = i - (len - 1);
				r = i + (len - 1);
			}
		}

		l = -1, r = -1;
		for (int i = 0; i < n; ++i) {
			int len = 0;
			evens[i] = 0;
			if (i < r) {
				len = min(r - i + 1, evens[l + (r - i) + 1]);
			}
			while (i - len - 1 >= 0 && i + len < n && s[i - len - 1] == s[i + len]) {
				++len;
			}
			evens[i] = len;
			if (i + len - 1 > r) {
				l = i - len;
				r = i + (len - 1);
			}
		}

		long long res = 0;
		for (int i = 0; i < n; ++i) {
			if (s[i][0] >= 0) {
				res += odds[i];
			}
			res += evens[i];
		}

		return res;
	}
};

int main(int argc, char** argv) {
    ios::sync_with_stdio(false);
    cin.tie(0);

    // freopen("output.txt", "r", stdin);

    Solution sol;
    int n, m;
    cin >> n >> m;
    vector<string> strs(n);
    for (int i = 0; i < n; ++i) {
    	cin >> strs[i];
    }

    cout << sol.count(strs, n, m) << endl;

    return 0;
}


#include <vector>
#include <stack>
#include <queue>
#include <list>
#include <bitset>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <string>
#include <chrono>
#include <random>
#include <cmath>
#include <cassert>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <functional>

using namespace std;

class Hash {
public:
	Hash(vector<pair<long long, long long>> pars = {}) {
		if (pars.empty() || pars.size() < 2) {
			mods.clear();
			mods.emplace_back(53, 1e9 + 7);
			mods.emplace_back(91, 998483569);
		} else {
			mods = pars;
			mods.resize(2);
		}
	}

	long long hash(vector<long long>& nums) {
		long long res = 0;
		int n = nums.size();
		long long hi = 0;
		long long lo = 0;

		long long p = mods[0].first;
		long long M = mods[0].second;
		// for (int i = 0; i < n; ++i) {
		// 	hi = (hi * p) % M + nums[i];
		// 	if (hi >= M) {
		// 		hi -= M;
		// 	}
		// }

		p = mods[1].first;
		M = mods[1].second;
		for (int i = 0; i < n; ++i) {
			lo = (lo * p) % M + nums[i];
			if (lo >= M) {
				lo -= M;
			}
		}

		res = hi << 16 | lo;
		return res;
	}

private:
	vector<pair<long long, long long>> mods;
};

class Solution {
public:
	long long count(vector<string>& strs, int n, int m) {
		vector<vector<vector<int>>> cnts(n, vector<vector<int>>(m + 1, vector<int>(26, 0)));

		for (int i = 0; i < n; ++i) {
			auto& vec = cnts[i];
			for (int j = 0; j < m; ++j) {
				for (int k = 0; k < 26; ++k) {
					vec[j + 1][k] = vec[j][k];
				}
				++vec[j + 1][strs[i][j] - 'a'];
			}
		}

		vector<long long> nums(n, 0);
		vector<vector<int>> stats(n, vector<int>(26, 0));
		vector<int> odds(n, 0);
		vector<int> evens(n, 0);

		long long bad = -1;
		long long res = 0;
		Hash hashFunc;
		for (int l = 1; l <= m; ++l) {
			for (int j = 0; j + l <= m; ++j) {
				int bads = 0;
				for (int i = 0; i < n; ++i) {
					int odds = 0;
					auto& vec = cnts[i];
					auto& stat = stats[i];
					for (int k = 0; k < 26; ++k) {
						stat[k] = vec[j + l][k] - vec[j][k];
						if ((stat[k] & 0x1) != 0) {
							++odds;
						}
					}
					if (odds <= 1) {
						// nums[i] = hashFunc.hash(stat);
						// nums[i] = 1;
					} else {
						// nums[i] = bad--;
						stat[0] = bad--;
						++bads;
					}
				}
				// cout << j << " " << j + l << " " << res << endl;
				res += countPadlindromes(stats, odds, evens) - bads;
			}
		}

		return res;
	}

private:
	long long countPadlindromes(vector<vector<int>>& s, vector<int>& odds, vector<int>& evens) {
		int n = s.size();

		int l = -1, r = -1;

		for (int i = 0; i < n; ++i) {
			int len = 0;
			if (i <= r) {
				len = min(r - i + 1, odds[l + r - i]);
			}
			while (i - len >= 0 && i + len < n && s[i - len] == s[i + len]) {
				++len;
			}
			odds[i] = len;
			if (i + len - 1 > r) {
				l = i - (len - 1);
				r = i + (len - 1);
			}
		}

		l = -1, r = -1;
		for (int i = 0; i < n; ++i) {
			int len = 0;
			if (i <= r) {
				len = min(r - i + 1, odds[l + (r - i) + 1]);
			}
			while (i - len - 1 >= 0 && i + len < n && s[i - len - 1] == s[i + len]) {
				++len;
			}
			evens[i] = len;
			if (i + len - 1 > r) {
				l = i - len;
				r = i + (len - 1);
			}
		}

		long long res = 0;
		for (int i = 0; i < n; ++i) {
			res += odds[i];
			res += evens[i];
		}

		return res;
	}
};

int main(int argc, char** argv) {
    ios::sync_with_stdio(false);
    cin.tie(0);

    // freopen("output.txt", "r", stdin);

    Solution sol;
    int n, m;
    cin >> n >> m;
    vector<string> strs(n);
    for (int i = 0; i < n; ++i) {
    	cin >> strs[i];
    }

    cout << sol.count(strs, n, m) << endl;

    return 0;
}


#include <vector>
#include <stack>
#include <queue>
#include <list>
#include <bitset>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <string>
#include <chrono>
#include <random>
#include <cmath>
#include <cassert>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <functional>

using namespace std;

class Hash {
public:
	Hash(vector<pair<long long, long long>> pars = {}) {
		if (pars.empty() || pars.size() < 2) {
			mods.clear();
			mods.emplace_back(53, 1e9 + 7);
			mods.emplace_back(91, 998483569);
		} else {
			mods = pars;
			mods.resize(2);
		}
	}

	long long hash(vector<long long>& nums) {
		long long res = 0;
		int n = nums.size();
		long long hi = 0;
		long long lo = 0;

		long long p = mods[0].first;
		long long M = mods[0].second;
		// for (int i = 0; i < n; ++i) {
		// 	hi = (hi * p) % M + nums[i];
		// 	if (hi >= M) {
		// 		hi -= M;
		// 	}
		// }

		p = mods[1].first;
		M = mods[1].second;
		for (int i = 0; i < n; ++i) {
			lo = (lo * p) % M + nums[i];
			if (lo >= M) {
				lo -= M;
			}
		}

		res = hi << 16 | lo;
		return res;
	}

private:
	vector<pair<long long, long long>> mods;
};

class Solution {
public:
	long long count(vector<string>& strs, int n, int m) {
		vector<vector<vector<int>>> cnts(n, vector<vector<int>>(m + 1, vector<int>(26, 0)));

		for (int i = 0; i < n; ++i) {
			auto& vec = cnts[i];
			for (int j = 0; j < m; ++j) {
				for (int k = 0; k < 26; ++k) {
					vec[j + 1][k] = vec[j][k];
				}
				++vec[j + 1][strs[i][j] - 'a'];
			}
		}

		vector<long long> nums(n, 0);
		vector<long long> stat(26, 0);
		long long bad = -1;
		long long res = 0;
		Hash hashFunc;
		for (int l = 1; l <= m; ++l) {
			for (int j = 0; j + l <= m; ++j) {
				int bads = 0;
				for (int i = 0; i < n; ++i) {
					int odds = 0;
					auto& vec = cnts[i];
					for (int k = 0; k < 26; ++k) {
						stat[k] = vec[j + l][k] - vec[j][k];
						if ((stat[k] & 0x1) != 0) {
							++odds;
						}
					}
					if (odds <= 1) {
						nums[i] = hashFunc.hash(stat);
						// nums[i] = 1;
					} else {
						nums[i] = bad--;
						++bads;
					}
				}
				// cout << j << " " << j + l << " " << res << endl;
				res += countPadlindromes(nums) - bads;
			}
		}

		return res;
	}

private:
	long long countPadlindromes(vector<long long>& s) {
		int n = s.size();
		vector<int> odds(n, 0);
		vector<int> evens(n, 0);

		int l = -1, r = -1;

		for (int i = 0; i < n; ++i) {
			int len = 0;
			if (i <= r) {
				len = min(r - i + 1, odds[l + r - i]);
			}
			while (i - len >= 0 && i + len < n && s[i - len] == s[i + len]) {
				++len;
			}
			odds[i] = len;
			if (i + len - 1 > r) {
				l = i - (len - 1);
				r = i + (len - 1);
			}
		}

		l = -1, r = -1;
		for (int i = 0; i < n; ++i) {
			int len = 0;
			if (i <= r) {
				len = min(r - i + 1, odds[l + (r - i) + 1]);
			}
			while (i - len - 1 >= 0 && i + len < n && s[i - len - 1] == s[i + len]) {
				++len;
			}
			evens[i] = len;
			if (i + len - 1 > r) {
				l = i - len;
				r = i + (len - 1);
			}
		}

		long long res = 0;
		for (int i = 0; i < n; ++i) {
			res += odds[i];
			res += evens[i];
		}

		return res;
	}
};

int main(int argc, char** argv) {
    ios::sync_with_stdio(false);
    cin.tie(0);

    Solution sol;
    int n, m;
    cin >> n >> m;
    vector<string> strs(n);
    for (int i = 0; i < n; ++i) {
    	cin >> strs[i];
    }

    cout << sol.count(strs, n, m) << endl;

    return 0;
}


#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<stack>
#include<cmath>
using namespace std;

typedef double DL;
typedef long long LL;
typedef unsigned int ui;

const int maxn = 300;
const ui INF = 2147483648;

char a[maxn][maxn];
ui s[maxn << 1],t[maxn << 1],pw[maxn],ha[maxn][maxn];
int n,m,p[maxn << 1],cnt[maxn][maxn][30];
LL ans;

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline void manacher(int l,int r,int n)
{
	// if (l == 1 && r == 3) puts("crzakioi");
	if (!n) return;
	for (int i = 1; i <= 2 * n + 1; i++)
		if (i & 1) s[i] = INF;
		else s[i] = t[i >> 1];

	s[0] = INF + 1;
	int pos = 1,mx = 0;
	for (int i = 1; i <= 2 * n + 1; i++)
	{
		p[i] = min(pos + mx - i,p[2 * pos - i]);
		while (s[i + p[i]] == s[i - p[i]]) p[i]++;
		if (pos + mx < i + p[i]) pos = i , mx = p[i];
	}

	for (int i = 1; i <= 2 * n + 1; i++)
		ans += p[i] >> 1;
}

int main()
{
	#ifdef AMC
		freopen("AMC1.txt","r",stdin);
		// freopen("AMC2.txt","w",stdout);
	#endif
	n = getint(); m = getint();

	pw[0] = 1;
	for (int i = 1; i <= m; i++) pw[i] = pw[i - 1] * 257;

	for (int i = 1; i <= n; i++)
	{
		scanf("%s",a[i] + 1);
		for (int j = 1; j <= m; j++)
		{
			for (int k = 0; k <= 25; k++)
				cnt[i][j][k] = cnt[i][j - 1][k];
			cnt[i][j][a[i][j] - 'a']++;
			ha[i][j] = ha[i][j - 1] + pw[a[i][j] - 'a'];
		}
	}

	for (int i = 1; i <= m; i++)
		for (int j = i; j <= m; j++)
		{
			LL pre = ans;
			int pos = 0;
			for (int k = 1; k <= n; k++)
			{
				int flag = 0;
				for (int p = 0; p <= 25; p++)
				{
					flag += cnt[k][j][p] - cnt[k][i - 1][p] == 1;
					if (flag >= 2) break;
				}

				if (((j - i + 1 & 1) && (flag <= 1)) || (!(j - i + 1 & 1) && (flag == 0)))
				{
					// if (i == 1 && j == 3)
					// {
						// printf("%d\n",k);
					// }
					t[k - pos] = ha[k][j] - ha[k][i - 1];
				}
				else
					manacher(i,j,k - 1 - pos) , pos = k;
			}
			manacher(i,j,n - pos);
			// printf("%d %d %lld\n",i,j,ans - pre);
		}

	printf("%lld\n",ans);
	return 0;
}


#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<stack>
#include<cmath>
using namespace std;

typedef double DL;
typedef long long LL;
typedef unsigned int ui;

const int maxn = 300;
const ui INF = 2147483648;

char a[maxn][maxn];
ui s[maxn << 1],t[maxn << 1],pw[maxn],ha[maxn][maxn];
int n,m,p[maxn << 1],cnt[maxn][maxn][30];
LL ans;

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline void manacher(int l,int r,int n)
{
	// if (l == 1 && r == 3) puts("crzakioi");
	if (!n) return;
	for (int i = 1; i <= 2 * n + 1; i++)
		if (i & 1) s[i] = INF; else s[i] = t[i >> 1];

	s[0] = INF + 1;
	int pos = 1,mx = 0;
	for (int i = 1; i <= 2 * n + 1; i++)
	{
		p[i] = min(pos + mx - i,p[2 * pos - i]);
		while (s[i + p[i]] == s[i - p[i]]) p[i]++;
		if (pos + mx < i + p[i]) pos = i , mx = p[i];
	}

	for (int i = 1; i <= 2 * n + 1; i++)
		ans += p[i] >> 1;
}

int main()
{
	#ifdef AMC
		freopen("AMC1.txt","r",stdin);
		// freopen("AMC2.txt","w",stdout);
	#endif
	n = getint(); m = getint();

	pw[0] = 1;
	for (int i = 1; i <= m; i++) pw[i] = pw[i - 1] * 257;

	for (int i = 1; i <= n; i++)
	{
		scanf("%s",a[i] + 1);
		for (int j = 1; j <= m; j++)
		{
			for (int k = 0; k <= 25; k++)
				cnt[i][j][k] = cnt[i][j - 1][k];
			cnt[i][j][a[i][j] - 'a']++;
			ha[i][j] = ha[i][j - 1] + pw[a[i][j] - 'a'];
		}
	}

	for (int i = 1; i <= m; i++)
		for (int j = i; j <= m; j++)
		{
			LL pre = ans;
			int pos = 0;
			for (int k = 1; k <= n; k++)
			{
				int flag = 0;
				for (int p = 0; p <= 25; p++)
				{
					flag += cnt[k][j][p] - cnt[k][i - 1][p] & 1;
					if (flag >= 2) break;
				}

				if (((j - i + 1 & 1) && (flag <= 1)) || (!(j - i + 1 & 1) && (flag == 0)))
				{
					// if (i == 1 && j == 3)
					// {
						// printf("%d\n",k);
					// }
					t[k - pos] = ha[k][j] - ha[k][i - 1];
				}
				else
					manacher(i,j,k - 1 - pos) , pos = k;
			}
			manacher(i,j,n - pos);
			// printf("%d %d %lld\n",i,j,ans - pre);
		}

	printf("%lld\n",ans);
	return 0;
}


#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<stack>
#include<cmath>
using namespace std;

typedef double DL;
typedef long long LL;
typedef unsigned int ui;

const int maxn = 300;
const ui INF = 2147483648;

char a[maxn][maxn];
ui s[maxn << 1],t[maxn << 1],pw[maxn],ha[maxn][maxn];
int n,m,p[maxn << 1],cnt[maxn][maxn][30];
LL ans;

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline int cmp(int p,int q,int l,int r)
{
	if ((p & 1) && (q & 1)) return 1;
	if ((p & 1) ^ (q & 1)) return 0;
	if (p == 0 || q == 0) return 0;

	p >>= 1; q >>= 1;
	for (int i = 0; i <= 25; i++)
		if (cnt[p][r][i] - cnt[p][l - 1][i] != cnt[q][r][i] - cnt[q][l - 1][i])
			return 0;

	return 1;
}

inline void manacher(int l,int r,int n)
{
	// if (l == 1 && r == 3) puts("crzakioi");
	if (!n) return;
	for (int i = 1; i <= 2 * n + 1; i++)
		if (i & 1) s[i] = INF; else s[i] = t[i >> 1];

	s[0] = INF + 1;
	int pos = 1,mx = 0;
	for (int i = 1; i <= 2 * n + 1; i++)
	{
		p[i] = min(pos + mx - i,p[2 * pos - i]);
		while (cmp(i + p[i],i - p[i],l,r)) p[i]++;
		if (pos + mx < i + p[i]) pos = i , mx = p[i];
	}

	for (int i = 1; i <= 2 * n + 1; i++)
		ans += p[i] >> 1;
}

int main()
{
	#ifdef AMC
		freopen("AMC1.txt","r",stdin);
		// freopen("AMC2.txt","w",stdout);
	#endif
	n = getint(); m = getint();

	pw[0] = 1;
	// for (int i = 1; i <= m; i++) pw[i] = pw[i - 1] * 257;

	for (int i = 1; i <= n; i++)
	{
		scanf("%s",a[i] + 1);
		for (int j = 1; j <= m; j++)
		{
			for (int k = 0; k <= 25; k++)
				cnt[i][j][k] = cnt[i][j - 1][k];
			cnt[i][j][a[i][j] - 'a']++;
			ha[i][j] = ha[i][j - 1] + pw[a[i][j] - 'a'];
		}
	}

	for (int i = 1; i <= m; i++)
		for (int j = i; j <= m; j++)
		{
			LL pre = ans;
			int pos = 0;
			for (int k = 1; k <= n; k++)
			{
				int flag = 0;
				for (int p = 0; p <= 25; p++)
				{
					flag += cnt[k][j][p] - cnt[k][i - 1][p] & 1;
					if (flag >= 2) break;
				}

				if (((j - i + 1 & 1) && (flag == 1)) || (!(j - i + 1 & 1) && (flag == 0)))
				{
					// if (i == 1 && j == 3)
					// {
						// printf("%d\n",k);
					// }
					t[k - pos] = ha[k][j] - ha[k][i - 1];
				}
				else
					manacher(i,j,k - 1 - pos) , pos = k;
			}
			manacher(i,j,n - pos);
			// printf("%d %d %lld\n",i,j,ans - pre);
		}

	printf("%lld\n",ans);
	return 0;
}


#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<stack>
#include<cmath>
using namespace std;

typedef double DL;
typedef long long LL;

const int maxn = 300;

char a[maxn][maxn];
int n,m,p[maxn << 1];
int cnt[maxn][maxn][30],c[maxn][30];
LL ans;

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline int cmp(int p,int q)
{
	if ((p & 1) && (q & 1)) return 1;
	if ((p & 1) ^ (q & 1)) return 0;
	if (p == 0 || q == 0) return 0;

	p >>= 1; q >>= 1;
	for (int i = 0; i <= 25; i++)
		if (c[p][i] != c[q][i])
			return 0;

	return 1;
}

inline void manacher(int l,int r,int n)
{
	// if (l == 1 && r == 3) puts("crzakioi");
	if (!n) return;
	for (int i = 0; i <= 25; i++) c[n + 1][i] = 0;

	int pos = 1,mx = 0;
	for (int i = 1; i <= 2 * n + 1; i++)
	{
		p[i] = min(pos + mx - i,p[2 * pos - i]);
		while (cmp(i + p[i],i - p[i])) p[i]++;
		if (pos + mx < i + p[i]) pos = i , mx = p[i];
	}

	// if (l == 1 && r == 2)
	// {
		// puts("use");
		// for (int i = 1; i <= 2 * n + 1; i++)
			// printf("%d ",p[i]);
		// putchar('\n');
	// }

	for (int i = 1; i <= 2 * n + 1; i++)
		ans += p[i] >> 1;
}

int main()
{
	#ifdef AMC
		freopen("AMC1.txt","r",stdin);
		freopen("AMC2.txt","w",stdout);
	#endif
	n = getint(); m = getint();
	// printf("%d\n",n);

	for (int i = 1; i <= n; i++)
	{
		scanf("%s",a[i] + 1);
		for (int j = 1; j <= m; j++)
		{
			for (int k = 0; k <= 25; k++)
				cnt[i][j][k] = cnt[i][j - 1][k];
			cnt[i][j][a[i][j] - 'a']++;
		}
	}

	for (int i = 1; i <= m; i++)
		for (int j = i; j <= m; j++)
		{
			LL pre = ans;
			int pos = 0;
			for (int k = 1; k <= n; k++)
			{
				int flag = 0;
				for (int p = 0; p <= 25; p++)
				{
					flag += cnt[k][j][p] - cnt[k][i - 1][p] & 1;
					if (flag >= 2) break;
				}

				if (((j - i + 1 & 1) && (flag == 1)) || (!(j - i + 1 & 1) && (flag == 0)))
					for (int p = 0; p <= 25; p++)
						c[k - pos][p] = cnt[k][j][p] - cnt[k][i - 1][p];
				else
					manacher(i,j,k - 1 - pos) , pos = k;
			}
			manacher(i,j,n - pos);
			// printf("%d %d %lld\n",i,j,ans - pre);
		}

	printf("%lld\n",ans);
	return 0;
}


#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<stack>
#include<cmath>
using namespace std;

typedef double DL;
typedef long long LL;
typedef unsigned int ui;

const int maxn = 300;
const ui INF = 2147483648;

char a[maxn][maxn];
ui s[maxn],t[maxn],pw[maxn],ha[maxn][maxn];
int n,m,p[maxn],cnt[maxn][maxn][30];
LL ans;

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline void manacher(int l,int r,int n)
{
	// if (l == 1 && r == 3) puts("crzakioi");
	if (!n) return;
	for (int i = 1; i <= 2 * n + 1; i++)
		if (i & 1) s[i] = INF;
		else s[i] = t[i >> 1];

	s[0] = INF + 1;
	int pos = 1,mx = 0;
	for (int i = 1; i <= 2 * n + 1; i++)
	{
		p[i] = min(pos + mx - i,p[2 * pos - i]);
		while (s[i + p[i]] == s[i - p[i]]) p[i]++;
		if (pos + mx < i + p[i]) pos = i , mx = p[i];
	}

	for (int i = 1; i <= 2 * n + 1; i++)
		ans += p[i] >> 1;
}

int main()
{
	#ifdef AMC
		freopen("AMC1.txt","r",stdin);
		// freopen("AMC2.txt","w",stdout);
	#endif
	n = getint(); m = getint();

	pw[0] = 1;
	for (int i = 1; i <= m; i++) pw[i] = pw[i - 1] * 257;

	for (int i = 1; i <= n; i++)
	{
		scanf("%s",a[i] + 1);
		for (int j = 1; j <= m; j++)
		{
			for (int k = 0; k <= 25; k++)
				cnt[i][j][k] = cnt[i][j - 1][k];
			cnt[i][j][a[i][j] - 'a']++;
			ha[i][j] = ha[i][j - 1] + pw[a[i][j] - 'a'];
		}
	}

	for (int i = 1; i <= m; i++)
		for (int j = i; j <= m; j++)
		{
			LL pre = ans;
			int pos = 0;
			for (int k = 1; k <= n; k++)
			{
				int flag = 0;
				for (int p = 0; p <= 25; p++)
				{
					flag += cnt[k][j][p] - cnt[k][i - 1][p] == 1;
					if (flag >= 2) break;
				}

				if (((j - i + 1 & 1) && (flag <= 1)) || (!(j - i + 1 & 1) && (flag == 0)))
				{
					// if (i == 1 && j == 3)
					// {
						// printf("%d\n",k);
					// }
					t[k - pos] = ha[k][j] - ha[k][i - 1];
				}
				else
					manacher(i,j,k - 1 - pos) , pos = k;
			}
			manacher(i,j,n - pos);
			// printf("%d %d %lld\n",i,j,ans - pre);
		}

	printf("%lld\n",ans);
	return 0;
}


#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<stack>
#include<cmath>
using namespace std;

typedef double DL;
typedef long long LL;
typedef unsigned int ui;

const int maxn = 300;
const ui INF = 2147483648;

char a[maxn][maxn];
ui s[maxn << 1],t[maxn << 1],pw[maxn],ha[maxn][maxn];
int n,m,p[maxn << 1],cnt[maxn][maxn][30];
LL ans;

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline void manacher(int l,int r,int n)
{
	// if (l == 1 && r == 3) puts("crzakioi");
	if (!n) return;
	for (int i = 1; i <= 2 * n + 1; i++)
		if (i & 1) s[i] = INF; else s[i] = t[i >> 1];

	s[0] = INF + 1;
	int pos = 1,mx = 0;
	for (int i = 1; i <= 2 * n + 1; i++)
	{
		p[i] = min(pos + mx - i,p[2 * pos - i]);
		while (s[i + p[i]] == s[i - p[i]]) p[i]++;
		if (pos + mx < i + p[i]) pos = i , mx = p[i];
	}

	for (int i = 1; i <= 2 * n + 1; i++)
		ans += p[i] >> 1;
}

int main()
{
	#ifdef AMC
		freopen("AMC1.txt","r",stdin);
		// freopen("AMC2.txt","w",stdout);
	#endif
	n = getint(); m = getint();

	pw[0] = 1;
	for (int i = 1; i <= m; i++) pw[i] = pw[i - 1] * 781;

	for (int i = 1; i <= n; i++)
	{
		scanf("%s",a[i] + 1);
		for (int j = 1; j <= m; j++)
		{
			for (int k = 0; k <= 25; k++)
				cnt[i][j][k] = cnt[i][j - 1][k];
			cnt[i][j][a[i][j] - 'a']++;
			ha[i][j] = ha[i][j - 1] + pw[a[i][j] - 'a'];
		}
	}

	for (int i = 1; i <= m; i++)
		for (int j = i; j <= m; j++)
		{
			LL pre = ans;
			int pos = 0;
			for (int k = 1; k <= n; k++)
			{
				int flag = 0;
				for (int p = 0; p <= 25; p++)
				{
					flag += cnt[k][j][p] - cnt[k][i - 1][p] & 1;
					if (flag >= 2) break;
				}

				if (((j - i + 1 & 1) && (flag == 1)) || (!(j - i + 1 & 1) && (flag == 0)))
				{
					// if (i == 1 && j == 3)
					// {
						// printf("%d\n",k);
					// }
					t[k - pos] = ha[k][j] - ha[k][i - 1];
				}
				else
					manacher(i,j,k - 1 - pos) , pos = k;
			}
			manacher(i,j,n - pos);
			// printf("%d %d %lld\n",i,j,ans - pre);
		}

	printf("%lld\n",ans);
	return 0;
}


#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<stack>
#include<cmath>
using namespace std;

typedef double DL;
typedef long long LL;
typedef unsigned int ui;

const int maxn = 300;
const ui INF = 2147483648;

char a[maxn][maxn];
ui s[maxn << 1],t[maxn << 1],pw[maxn],ha[maxn][maxn];
int n,m,p[maxn << 1],cnt[maxn][maxn][30],c[maxn][30];
LL ans;

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline int cmp(int p,int q)
{
	if ((p & 1) && (q & 1)) return 1;
	if ((p & 1) ^ (q & 1)) return 0;
	if (p == 0 || q == 0) return 0;

	p >>= 1; q >>= 1;
	for (int i = 0; i <= 25; i++)
		if (c[p][i] != c[q][i])
			return 0;

	return 1;
}

inline void manacher(int l,int r,int n)
{
	// if (l == 1 && r == 3) puts("crzakioi");
	if (!n) return;

	int pos = 1,mx = 0;
	for (int i = 1; i <= 2 * n + 1; i++)
	{
		p[i] = min(pos + mx - i,p[2 * pos - i]);
		while (cmp(i + p[i],i - p[i])) p[i]++;
		if (pos + mx < i + p[i]) pos = i , mx = p[i];
	}

	for (int i = 1; i <= 2 * n + 1; i++)
		ans += p[i] >> 1;
}

int main()
{
	#ifdef AMC
		freopen("AMC1.txt","r",stdin);
		// freopen("AMC2.txt","w",stdout);
	#endif
	n = getint(); m = getint();

	pw[0] = 1;
	// for (int i = 1; i <= m; i++) pw[i] = pw[i - 1] * 257;

	for (int i = 1; i <= n; i++)
	{
		scanf("%s",a[i] + 1);
		for (int j = 1; j <= m; j++)
		{
			for (int k = 0; k <= 25; k++)
				cnt[i][j][k] = cnt[i][j - 1][k];
			cnt[i][j][a[i][j] - 'a']++;
		}
	}

	for (int i = 1; i <= m; i++)
		for (int j = i; j <= m; j++)
		{
			// LL pre = ans;
			int pos = 0;
			for (int k = 1; k <= n; k++)
			{
				int flag = 0;
				for (int p = 0; p <= 25; p++)
				{
					flag += cnt[k][j][p] - cnt[k][i - 1][p] & 1;
					if (flag >= 2) break;
				}

				if (((j - i + 1 & 1) && (flag == 1)) || (!(j - i + 1 & 1) && (flag == 0)))
					for (int p = 0; p <= 25; p++)
						c[k - pos][p] = cnt[k][j][p] - cnt[k][i - 1][p];
				else
					manacher(i,j,k - 1 - pos) , pos = k;
			}
			manacher(i,j,n - pos);
			// printf("%d %d %lld\n",i,j,ans - pre);
		}

	printf("%lld\n",ans);
	return 0;
}


#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<stack>
#include<cmath>
using namespace std;

typedef double DL;
typedef long long LL;
typedef unsigned int ui;

const int maxn = 300;
const ui INF = 2147483648;

char a[maxn][maxn];
ui s[maxn << 1],t[maxn << 1],pw[maxn],ha[maxn][maxn];
int n,m,p[maxn << 1],cnt[maxn][maxn][30];
LL ans;

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline void manacher(int l,int r,int n)
{
	// if (l == 1 && r == 3) puts("crzakioi");
	if (!n) return;
	for (int i = 1; i <= 2 * n + 1; i++)
		if (i & 1) s[i] = INF;
		else s[i] = t[i >> 1];

	s[0] = INF + 1;
	int pos = 1,mx = 0;
	for (int i = 1; i <= 2 * n + 1; i++)
	{
		p[i] = min(pos + mx - i,p[2 * pos - i]);
		while (s[i + p[i]] == s[i - p[i]]) p[i]++;
		if (pos + mx < i + p[i]) pos = i , mx = p[i];
	}

	for (int i = 1; i <= 2 * n + 1; i++)
		ans += p[i] >> 1;
}

int main()
{
	#ifdef AMC
		freopen("AMC1.txt","r",stdin);
		// freopen("AMC2.txt","w",stdout);
	#endif
	n = getint(); m = getint();

	pw[0] = 1;
	for (int i = 1; i <= m; i++) pw[i] = pw[i - 1] * 781;

	for (int i = 1; i <= n; i++)
	{
		scanf("%s",a[i] + 1);
		for (int j = 1; j <= m; j++)
		{
			for (int k = 0; k <= 25; k++)
				cnt[i][j][k] = cnt[i][j - 1][k];
			cnt[i][j][a[i][j] - 'a']++;
			ha[i][j] = ha[i][j - 1] + pw[a[i][j] - 'a'];
		}
	}

	for (int i = 1; i <= m; i++)
		for (int j = i; j <= m; j++)
		{
			LL pre = ans;
			int pos = 0;
			for (int k = 1; k <= n; k++)
			{
				int flag = 0;
				for (int p = 0; p <= 25; p++)
				{
					flag += cnt[k][j][p] - cnt[k][i - 1][p] == 1;
					if (flag >= 2) break;
				}

				if (((j - i + 1 & 1) && (flag <= 1)) || (!(j - i + 1 & 1) && (flag == 0)))
				{
					// if (i == 1 && j == 3)
					// {
						// printf("%d\n",k);
					// }
					t[k - pos] = ha[k][j] - ha[k][i - 1];
				}
				else
					manacher(i,j,k - 1 - pos) , pos = k;
			}
			manacher(i,j,n - pos);
			// printf("%d %d %lld\n",i,j,ans - pre);
		}

	printf("%lld\n",ans);
	return 0;
}


#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<stack>
#include<cmath>
using namespace std;

typedef double DL;
typedef long long LL;
typedef unsigned long long ui;

const int maxn = 300;
const ui INF = 2147483648;

char a[maxn][maxn];
ui s[maxn << 1],t[maxn << 1],pw[maxn],ha[maxn][maxn];
int n,m,p[maxn << 1],cnt[maxn][maxn][30];
LL ans;

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline void manacher(int l,int r,int n)
{
	// if (l == 1 && r == 3) puts("crzakioi");
	if (!n) return;
	for (int i = 1; i <= 2 * n + 1; i++)
		if (i & 1) s[i] = INF; else s[i] = t[i >> 1];

	s[0] = INF + 1;
	int pos = 1,mx = 0;
	for (int i = 1; i <= 2 * n + 1; i++)
	{
		p[i] = min(pos + mx - i,p[2 * pos - i]);
		while (s[i + p[i]] == s[i - p[i]]) p[i]++;
		if (pos + mx < i + p[i]) pos = i , mx = p[i];
	}

	for (int i = 1; i <= 2 * n + 1; i++)
		ans += p[i] >> 1;
}

int main()
{
	#ifdef AMC
		freopen("AMC1.txt","r",stdin);
		// freopen("AMC2.txt","w",stdout);
	#endif
	n = getint(); m = getint();

	pw[0] = 1;
	for (int i = 1; i <= m; i++) pw[i] = pw[i - 1] * 257;

	for (int i = 1; i <= n; i++)
	{
		scanf("%s",a[i] + 1);
		for (int j = 1; j <= m; j++)
		{
			for (int k = 0; k <= 25; k++)
				cnt[i][j][k] = cnt[i][j - 1][k];
			cnt[i][j][a[i][j] - 'a']++;
			ha[i][j] = ha[i][j - 1] + pw[a[i][j] - 'a'];
		}
	}

	for (int i = 1; i <= m; i++)
		for (int j = i; j <= m; j++)
		{
			LL pre = ans;
			int pos = 0;
			for (int k = 1; k <= n; k++)
			{
				int flag = 0;
				for (int p = 0; p <= 25; p++)
				{
					flag += cnt[k][j][p] - cnt[k][i - 1][p] == 1;
					if (flag >= 2) break;
				}

				if (((j - i + 1 & 1) && (flag <= 1)) || (!(j - i + 1 & 1) && (flag == 0)))
				{
					// if (i == 1 && j == 3)
					// {
						// printf("%d\n",k);
					// }
					t[k - pos] = ha[k][j] - ha[k][i - 1];
				}
				else
					manacher(i,j,k - 1 - pos) , pos = k;
			}
			manacher(i,j,n - pos);
			// printf("%d %d %lld\n",i,j,ans - pre);
		}

	printf("%lld\n",ans);
	return 0;
}


#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<stack>
#include<cmath>
using namespace std;

typedef double DL;
typedef long long LL;
typedef unsigned int ui;

const int maxn = 300;
const ui INF = 2147483648;

char a[maxn][maxn];
ui s[maxn << 1],t[maxn << 1],pw[maxn],ha[maxn][maxn];
int n,m,p[maxn << 1],cnt[maxn][maxn][30];
LL ans;

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline void manacher(int l,int r,int n)
{
	// if (l == 1 && r == 3) puts("crzakioi");
	if (!n) return;
	for (int i = 1; i <= 2 * n + 1; i++)
		if (i & 1) s[i] = INF; else s[i] = t[i >> 1];

	s[0] = INF + 1;
	int pos = 1,mx = 0;
	for (int i = 1; i <= 2 * n + 1; i++)
	{
		p[i] = min(pos + mx - i,p[2 * pos - i]);
		while (s[i + p[i]] == s[i - p[i]]) p[i]++;
		if (pos + mx < i + p[i]) pos = i , mx = p[i];
	}

	for (int i = 1; i <= 2 * n + 1; i++)
		ans += p[i] >> 1;
}

int main()
{
	#ifdef AMC
		freopen("AMC1.txt","r",stdin);
		// freopen("AMC2.txt","w",stdout);
	#endif
	n = getint(); m = getint();

	pw[0] = 1;
	for (int i = 1; i <= m; i++) pw[i] = pw[i - 1] * 257;

	for (int i = 1; i <= n; i++)
	{
		scanf("%s",a[i] + 1);
		for (int j = 1; j <= m; j++)
		{
			for (int k = 0; k <= 25; k++)
				cnt[i][j][k] = cnt[i][j - 1][k];
			cnt[i][j][a[i][j] - 'a']++;
			ha[i][j] = ha[i][j - 1] + pw[a[i][j] - 'a'];
		}
	}

	for (int i = 1; i <= m; i++)
		for (int j = i; j <= m; j++)
		{
			LL pre = ans;
			int pos = 0;
			for (int k = 1; k <= n; k++)
			{
				int flag = 0;
				for (int p = 0; p <= 25; p++)
				{
					flag += cnt[k][j][p] - cnt[k][i - 1][p] & 1;
					if (flag >= 2) break;
				}

				if (((j - i + 1 & 1) && (flag == 1)) || (!(j - i + 1 & 1) && (flag == 0)))
				{
					// if (i == 1 && j == 3)
					// {
						// printf("%d\n",k);
					// }
					t[k - pos] = ha[k][j] - ha[k][i - 1];
				}
				else
					manacher(i,j,k - 1 - pos) , pos = k;
			}
			manacher(i,j,n - pos);
			// printf("%d %d %lld\n",i,j,ans - pre);
		}

	printf("%lld\n",ans);
	return 0;
}


//#pragma comment(linker, "/stack:200000000")
//#pragma GCC optimize("Ofast")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#include <bits/stdc++.h>
using namespace std;
#pragma GCC diagnostic ignored "-Wmissing-declarations"
#define FINAL_OUT(x) {cout << (x) << '\n'; exit(0); }
inline int safe_mul(const int x, const int y) __attribute__ ((warn_unused_result));

const uint8_t TZ = 'z' - 'a' + 1;
using TMask = array<uint8_t, TZ>;

uint32_t getCount(const vector<TMask>& s) {
    if (s.empty())
        return 0;
    const int n = s.size();
    vector<int> d1(n, 0);
    int l=0, r=-1;
    for (int i=0; i<n; ++i) {
        int k = (i>r ? 1 : min (d1[l+r-i], r-i));
        while (i+k < n && i-k >= 0 && s[i+k] == s[i-k])  ++k;
        d1[i] = k--;
        if (i+k > r)
            l = i-k, r = i+k;
    }

    vector<int> d2 (n, 0);
    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 < n && i-k-1 >= 0 && s[i+k] == s[i-k-1])  ++k;
        d2[i] = k--;
        if (i+k > r)
            l = i-k - 1, r = i+k;
    }

    return accumulate(begin(d1), end(d1), 0u) + accumulate(begin(d2), end(d2), 0u);
}

TMask operator-(const TMask& lhs, const TMask& rhs) {
    TMask result;
    transform(begin(lhs), end(lhs), begin(rhs), begin(result), [](const auto l, const auto r){return l - r;});
    return result;
}

bool IsGood(const TMask& cur) {
    return count_if(begin(cur), end(cur), [](const auto x) {return x%2 == 1;}) <= 1;
}

int main()
{
//    freopen("in.txt","r", stdin);
//    freopen("out.txt", "w", stdout);
//    cout << fixed << setprecision(15);
    ios_base::sync_with_stdio(false);

    int n,m;
    cin >> n >> m;

    vector<vector<TMask>> mask(n, vector<TMask>(m + 1, {0}));
    for(int i = 0; i < n; ++i) {
        string s;
        cin >> s;
        for(int j = 0; j < m; ++j) {
            mask[i][j + 1] = mask[i][j];
            ++mask[i][j + 1][s[j] - 'a'];
        }
    }

    uint32_t result = 0;
    for(int l = 0; l < m; ++l)
    for(int r = l + 1; r <= m; ++r) {
        vector<TMask> cur;
        for(int i = 0; i < n; ++i) {
            auto tmp = mask[i][r] - mask[i][l];
            if (!IsGood(tmp)) {
                result += getCount(cur);
                cur.clear();
            } else {
                cur.push_back(tmp);
            }
        }
        result += getCount(cur);
    }
    cout << result << endl;
}


//#pragma comment(linker, "/stack:200000000")
//#pragma GCC optimize("Ofast")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#include <bits/stdc++.h>
using namespace std;
#pragma GCC diagnostic ignored "-Wmissing-declarations"
#define FINAL_OUT(x) {cout << (x) << '\n'; exit(0); }
inline int safe_mul(const int x, const int y) __attribute__ ((warn_unused_result));

const uint32_t TZ = 'z' - 'a' + 1;
using TMask = array<uint32_t, TZ>;

uint32_t getCount(const vector<TMask>& s) {
    const int n = s.size();
    vector<int> d1(n);
    int l=0, r=-1;
    for (int i=0; i<n; ++i) {
        int k = (i>r ? 0 : min (d1[l+r-i], r-i)) + 1;
        while (i+k < n && i-k >= 0 && s[i+k] == s[i-k])  ++k;
        d1[i] = k--;
        if (i+k > r)
            l = i-k,  r = i+k;
    }

    vector<int> d2 (n);
    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)) + 1;
        while (i+k-1 < n && i-k >= 0 && s[i+k-1] == s[i-k])  ++k;
        d2[i] = --k;
        if (i+k-1 > r)
            l = i-k,  r = i+k-1;
    }

    uint32_t result = 0;
    for(const int x : d1)
        result += x;
    for(const int x : d2)
        result += x;

    return result;
}

TMask operator-(const TMask& lhs, const TMask& rhs) {
    TMask result;
    for(uint8_t i = 0; i < TZ; ++i)
        result[i] = lhs[i] - rhs[i];
    return result;
}

bool IsGood(const TMask& cur) {
    return count_if(begin(cur), end(cur), [](const int x) {return x&1 == 1;}) <= 1;
}

int main()
{
//    freopen("in.txt","r", stdin);
//    freopen("out.txt", "w", stdout);
//    cout << fixed << setprecision(15);
    ios_base::sync_with_stdio(false);

    int n,m;
    cin >> n >> m;

    vector<vector<TMask> > mask(n, vector<TMask>(m + 1));
    for(int i = 0; i < n; ++i) {
        string s;
        cin >> s;
        for(int j = 0; j < m; ++j) {
            mask[i][j + 1] = mask[i][j];
            ++mask[i][j + 1][s[j] - 'a'];
        }
    }

    uint32_t result = 0;
    for(int l = 0; l < m; ++l)
    for(int r = l + 1; r <= m; ++r) {
        vector<TMask> cur;
        for(int i = 0; i < n; ++i) {
            auto tmp = mask[i][r] - mask[i][l];
            if (!IsGood(tmp)) {
                result += getCount(cur);
                cur.clear();
            } else {
                cur.push_back(tmp);
            }
        }
        result += getCount(cur);
    }
    cout << result << endl;
}


//#pragma comment(linker, "/stack:200000000")
//#pragma GCC optimize("Ofast")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#include <bits/stdc++.h>
using namespace std;
#pragma GCC diagnostic ignored "-Wmissing-declarations"
#define FINAL_OUT(x) {cout << (x) << '\n'; exit(0); }
inline int safe_mul(const int x, const int y) __attribute__ ((warn_unused_result));

const uint8_t TZ = 'z' - 'a' + 1;
using TMask = array<uint8_t, TZ>;

uint32_t getCount(const vector<TMask>& s) {
    if (s.empty())
        return 0;
    const int n = s.size();
    vector<int> d1(n, 0);
    int l=0, r=-1;
    for (int i=0; i<n; ++i) {
        int k = (i>r ? 0 : min (d1[l+r-i], r-i)) + 1;
        while (i+k < n && i-k >= 0 && s[i+k] == s[i-k])  ++k;
        d1[i] = k--;
        if (i+k > r)
            l = i-k, r = i+k;
    }

    vector<int> d2 (n, 0);
    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)) + 1;
        while (i+k-1 < n && i-k >= 0 && s[i+k-1] == s[i-k])  ++k;
        d2[i] = --k;
        if (i+k-1 > r)
            l = i-k, r = i+k-1;
    }

    return accumulate(begin(d1), end(d1), 0u) + accumulate(begin(d2), end(d2), 0u);
}

TMask operator-(const TMask& lhs, const TMask& rhs) {
    TMask result;
    transform(begin(lhs), end(lhs), begin(rhs), begin(result), [](const auto l, const auto r){return l - r;});
    return result;
}

bool IsGood(const TMask& cur) {
    return count_if(begin(cur), end(cur), [](const auto x) {return x%2 == 1;}) <= 1;
}

int main()
{
//    freopen("in.txt","r", stdin);
//    freopen("out.txt", "w", stdout);
//    cout << fixed << setprecision(15);
    ios_base::sync_with_stdio(false);

    int n,m;
    cin >> n >> m;

    vector<vector<TMask>> mask(n, vector<TMask>(m + 1, {0}));
    for(int i = 0; i < n; ++i) {
        string s;
        cin >> s;
        for(int j = 0; j < m; ++j) {
            mask[i][j + 1] = mask[i][j];
            ++mask[i][j + 1][s[j] - 'a'];
        }
    }

    uint32_t result = 0;
    for(int l = 0; l < m; ++l)
    for(int r = l + 1; r <= m; ++r) {
        vector<TMask> cur;
        for(int i = 0; i < n; ++i) {
            auto tmp = mask[i][r] - mask[i][l];
            if (!IsGood(tmp)) {
                result += getCount(cur);
                cur.clear();
            } else {
                cur.push_back(tmp);
            }
        }
        result += getCount(cur);
    }
    cout << result << endl;
}


//#pragma comment(linker, "/stack:200000000")
//#pragma GCC optimize("Ofast")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#include <bits/stdc++.h>
using namespace std;
#pragma GCC diagnostic ignored "-Wmissing-declarations"
#define FINAL_OUT(x) {cout << (x) << '\n'; exit(0); }
inline int safe_mul(const int x, const int y) __attribute__ ((warn_unused_result));

const uint8_t TZ = 'z' - 'a' + 1;
using TMask = array<uint8_t, TZ>;

uint32_t getCount(const vector<TMask>& s) {
    if (s.empty())
        return 0;
    const int n = s.size();
    vector<int> d1(n, 0);
    int l=0, r=-1;
    for (int i=0; i<n; ++i) {
        int k = (i>r ? 0 : min (d1[l+r-i], r-i)) + 1;
        while (i+k < n && i-k >= 0 && s[i+k] == s[i-k])  ++k;
        d1[i] = k--;
        if (i+k > r)
            l = i-k,  r = i+k;
    }

    vector<int> d2 (n, 0);
    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)) + 1;
        while (i+k-1 < n && i-k >= 0 && s[i+k-1] == s[i-k])  ++k;
        d2[i] = --k;
        if (i+k-1 > r)
            l = i-k,  r = i+k-1;
    }

    uint32_t result = 0;
    for(const int x : d1)
        result += x;
    for(const int x : d2)
        result += x;

    return result;
}

TMask operator-(const TMask& lhs, const TMask& rhs) {
    TMask result;
    for(uint8_t i = 0; i < TZ; ++i)
        result[i] = lhs[i] - rhs[i];
    return result;
}

bool IsGood(const TMask& cur) {
    return count_if(begin(cur), end(cur), [](const uint8_t x) {return x%2 == 1;}) <= 1;
}

int main()
{
//    freopen("in.txt","r", stdin);
//    freopen("out.txt", "w", stdout);
//    cout << fixed << setprecision(15);
    ios_base::sync_with_stdio(false);

    int n,m;
    cin >> n >> m;

    vector<vector<TMask>> mask(n, vector<TMask>(m + 1, {0}));
    for(int i = 0; i < n; ++i) {
        string s;
        cin >> s;
        for(int j = 0; j < m; ++j) {
            mask[i][j + 1] = mask[i][j];
            ++mask[i][j + 1][s[j] - 'a'];
        }
    }

    uint32_t result = 0;
    for(int l = 0; l < m; ++l)
    for(int r = l + 1; r <= m; ++r) {
        vector<TMask> cur;
        for(int i = 0; i < n; ++i) {
            auto tmp = mask[i][r] - mask[i][l];
            if (!IsGood(tmp)) {
                result += getCount(cur);
                cur.clear();
            } else {
                cur.push_back(tmp);
            }
        }
        result += getCount(cur);
    }
    cout << result << endl;
}


//#pragma comment(linker, "/stack:200000000")
//#pragma GCC optimize("Ofast")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#include <bits/stdc++.h>
using namespace std;
#pragma GCC diagnostic ignored "-Wmissing-declarations"
#define FINAL_OUT(x) {cout << (x) << '\n'; exit(0); }
inline int safe_mul(const int x, const int y) __attribute__ ((warn_unused_result));

const uint8_t TZ = 'z' - 'a' + 1;
using TMask = array<uint8_t, TZ>;

uint32_t getCount(const vector<TMask>& s) {
    if (s.empty())
        return 0;
    const int n = s.size();
    vector<int> d1(n, 0);
    int l=0, r=-1;
    for (int i=0; i<n; ++i) {
        int k = (i>r ? 0 : min (d1[l+r-i], r-i)) + 1;
        while (i+k < n && i-k >= 0 && s[i+k] == s[i-k])  ++k;
        d1[i] = k--;
        if (i+k > r)
            l = i-k,  r = i+k;
    }

    vector<int> d2 (n, 0);
    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)) + 1;
        while (i+k-1 < n && i-k >= 0 && s[i+k-1] == s[i-k])  ++k;
        d2[i] = --k;
        if (i+k-1 > r)
            l = i-k,  r = i+k-1;
    }

    uint32_t result = 0;
    for(const int x : d1)
        result += x;
    for(const int x : d2)
        result += x;

    return result;
}

TMask operator-(const TMask& lhs, const TMask& rhs) {
    TMask result;
    for(uint8_t i = 0; i < TZ; ++i)
        result[i] = lhs[i] - rhs[i];
    return result;
}

bool IsGood(const TMask& cur) {
    return count_if(begin(cur), end(cur), [](const int8_t x) {return x%2 == 1;}) <= 1;
}

int main()
{
//    freopen("in.txt","r", stdin);
//    freopen("out.txt", "w", stdout);
//    cout << fixed << setprecision(15);
    ios_base::sync_with_stdio(false);

    int n,m;
    cin >> n >> m;

    vector<vector<TMask> > mask(n, vector<TMask>(m + 1, {0}));
    for(int i = 0; i < n; ++i) {
        string s;
        cin >> s;
        for(int j = 0; j < m; ++j) {
            mask[i][j + 1] = mask[i][j];
            ++mask[i][j + 1][s[j] - 'a'];
        }
    }

    uint32_t result = 0;
    for(int l = 0; l < m; ++l)
    for(int r = l + 1; r <= m; ++r) {
        vector<TMask> cur;
        for(int i = 0; i < n; ++i) {
            auto tmp = mask[i][r] - mask[i][l];
            if (!IsGood(tmp)) {
                result += getCount(cur);
                cur.clear();
            } else {
                cur.push_back(tmp);
            }
        }
        result += getCount(cur);
    }
    cout << result << endl;
}


#include<bits/stdc++.h>
typedef long long ll;
#define F(i,L,R) for (ll i = L; i < R; i++)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define re(x) scanf("%lld",&x)
using namespace std;

ll key = 503, mod = 1e9+7;

ll solve(ll s, ll e, vector<ll> temp){
          ll n = e-s+1, ans = 0;
          vector<int> d1(n), d2(n);

          for(int t = 0, l = 0, r = -1;t<n;t++){
              int k=(t>r)?1:min(d1[l+r-t], r-t+1);

              while(t-k>=0 && t+k<n && temp[t-k+s] == temp[t+k+s]) k++;
              d1[t] = k--;
              ans += d1[t];
              if(t+k>r) l = t-k, r = t+k;
          }

          for(int t = 0, l = 0,r = -1; t<n;t++){
              int k = (t>r)?0: min(d2[l+r-t], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k-1+s] == temp[t+k+s] ) k++;
              d2[t] = k--;
              ans += d2[t];
              if(t+k>r) l = t-k-1, r = t+k;
          }

          return ans;
}

int main(){
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  ll n,m, ans = 0;
  cin>>n>>m;
  string a[n+1];
  F(i,1,n+1) cin>>a[i];
  vector<ll> pk(27);
  pk[0] = 1;
  F(i,1,27) pk[i] = (pk[i-1]*key)%mod;

  F(i,1,m+1){
  	  vector<ll> temp(n,0), mask(n,0);
      F(j,i,m+1){
          int isP[n];
          F(k,1,n+1){
              ll pos = (int)a[k][j-1]-'a';
              temp[k-1] = (temp[k-1]+pk[pos])%mod;
              mask[k-1] = mask[k-1]^(1<<pos);
              if(mask[k-1] == 0 || (mask[k-1]&(mask[k-1]-1)) == 0) isP[k-1] = 1;
              else isP[k-1] = 0;
          }
          // cout<<i<<" "<<j<<":"<<endl;
          // for(auto z: temp) cout<<z<<" ";
          // 	for(auto a: isP) cout<<a<<" ";
          // cout<<endl;
            F(k, 0, n){
                if(isP[k] == 0) continue;
                else{
                    ll st = k;
                    while(k<n && isP[k] == 1) k++;
                    ans += solve(st, k-1, temp);
                }
            }

      }
  }

  cout<<ans<<"\n";

  return 0;
}


#include<bits/stdc++.h>
typedef long long ll;
#define F(i,L,R) for (ll i = L; i < R; i++)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define re(x) scanf("%lld",&x)
using namespace std;

ll key = 503, mod = 1e9+7;

ll solve(ll s, ll e, vector<ll> temp){
          ll n = e-s+1, ans = 0;
          vector<int> d1(n), d2(n);

          for(int t = 0, l = 0, r = -1;t<n;t++){
              int k=(t>r)?1:min(d1[l+r-t], r-t+1);

              while(t-k>=0 && t+k<n && temp[t-k+s] == temp[t+k+s]) k++;
              d1[t] = k--;
              ans += d1[t];
              if(t+k>r) l = t-k, r = t+k;
          }

          for(int t = 0, l = 0,r = -1; t<n;t++){
              int k = (t>r)?0: min(d2[l+r-t+1], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k-1+s] == temp[t+k+s] ) k++;
              d2[t] = k--;
              ans += d2[t];
              if(t+k>r) l = t-k-1, r = t+k;
          }

          return ans;
}

int main(){
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  ll n,m, ans = 0;
  cin>>n>>m;
  string a[n+1];
  F(i,1,n+1) cin>>a[i];
  vector<ll> pk(27);
  pk[0] = 1;
  F(i,1,27) pk[i] = (pk[i-1]*key)%mod;

  F(i,1,m+1){
  	  vector<ll> temp(n,0), mask(n,0);
      F(j,i,m+1){
          int isP[n];
          F(k,1,n+1){
              ll pos = (int)a[k][j-1]-'a';
              temp[k-1] = (temp[k-1]+pk[pos])%mod;
              mask[k-1] = mask[k-1]^(1<<pos);
              if(mask[k-1] == 0 || (mask[k-1]&(mask[k-1]-1)) == 0) isP[k-1] = 1;
              else isP[k-1] = 0;
          }
          // cout<<i<<" "<<j<<":"<<endl;
          // for(auto z: temp) cout<<z<<" ";
          // 	for(auto a: isP) cout<<a<<" ";
          // cout<<endl;
            F(k, 0, n){
                if(isP[k] == 0) continue;
                else{
                    ll st = k;
                    while(k<n && isP[k] == 1) k++;
                    ans += solve(st, k-1, temp);
                }
            }

      }
  }

  cout<<ans<<"\n";

  return 0;
}


#include<bits/stdc++.h>
typedef long long ll;
#define F(i,L,R) for (ll i = L; i < R; i++)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define re(x) scanf("%lld",&x)
using namespace std;

ll key = 503, mod = 1e9+7;

int main(){
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  ll n,m, ans = 0;
  cin>>n>>m;
  string a[n+1];
  F(i,1,n+1) cin>>a[i];
  vector<ll> pk(27);
  pk[0] = 1;
  F(i,1,27) pk[i] = (pk[i-1]*key)%mod;

  F(i,1,m+1){
  	  vector<ll> temp(n,0), mask(n,0);
      F(j,i,m+1){
          int isP[n];
          F(k,1,n+1){
              ll pos = (int)a[k][j-1]-'a';
              temp[k-1] = (temp[k-1]+pk[pos])%mod;
              mask[k-1] = mask[k-1]^(1<<pos);
              if(mask[k-1] == 0 || (mask[k-1]&(mask[k-1]-1)) == 0) isP[k-1] = 1;
              else isP[k-1] = 0;
          }
          // cout<<i<<" "<<j<<":"<<endl;
          // for(auto z: temp) cout<<z<<" ";
          // 	for(auto a: isP) cout<<a<<" ";
          // cout<<endl;

          vector<int> d1(n), d2(n);

          for(int t = 0, l = 0, r = -1;t<n;t++){
              int k=(t>r)?1:min(d1[l+r-t], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k] == temp[t+k] && isP[t-k] && isP[t+k]) k++;
              d1[t] = k--;
              if(d1[t]>1|| (d1[t] == 1 && isP[t]))ans += d1[t];
              if(t+k>r) l = t-k, r = t+k;
          }

          for(int t = 0, l = 0,r = -1; t<n;t++){
              int k = (t>r)?0: min(d2[l+r-t], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k-1] == temp[t+k] && isP[t-k-1] && isP[t+k]) k++;
              d2[t] = k--;
              ans += d2[t];
              if(t+k>r) l = t-k-1, r = t+k;
          }

      }
  }

  cout<<ans<<"\n";

  return 0;
}


#include<bits/stdc++.h>
typedef long long ll;
#define F(i,L,R) for (ll i = L; i < R; i++)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define re(x) scanf("%lld",&x)
using namespace std;

ll key = 601, mod = 1e9+7;

int main(){
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  ll n,m, ans = 0;
  cin>>n>>m;
  string a[n+1];
  F(i,1,n+1) cin>>a[i];
  ll dp[n+1][m+1], cnt[n+1][m+1][26];
  memset(dp, 0, sizeof(dp));
  memset(cnt, 0, sizeof(cnt));
  vector<ll> pk(m+1);
  pk[0] = 1;
  F(i,1,n) pk[i] = (pk[i-1]*key)%mod;
  F(i,1,n+1){
      F(j,1,m+1){
          ll val = (int) a[i][j-1] -'a';
          dp[i][j] = (dp[i][j-1] + pk[val]) %mod;
          F(k,0,26) cnt[i][j][k] += cnt[i][j-1][k];
             cnt[i][j][(int)a[i][j-1]-'a']++;
      }
  }
  F(i,1,m+1){
      F(j,i,m+1){
          vector<ll> temp;
          int isP[n];
          F(k,1,n+1){
              ll cntOdd = 0;
              F(l,0,26) cntOdd +=  (cnt[k][j][l]-cnt[k][i-1][l])%2;
              ll h = (dp[k][j] - dp[k][i-1] +mod)%mod;
              if(cntOdd>1) isP[k-1] = 0;
              else isP[k-1] = 1;
              temp.pb(h);
          }
          // cout<<i<<" "<<j<<":"<<endl;
          // for(auto z: temp) cout<<z<<" ";
          	// for(auto a: isP) cout<<a<<" ";
          // cout<<endl;

          vector<int> d1(n), d2(n);

          for(int t = 0, l = 0, r = -1;t<n;t++){
              int k=(t>r)?1:min(d1[l+r-t], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k] == temp[t+k] && isP[t-k] && isP[t+k]) k++;
              d1[t] = k--;
              if(d1[t]>1|| (d1[t] == 1 && isP[t]))ans += d1[t];
              if(t+k>r) l = t-k, r = t+k;
          }

          for(int t = 0, l = 0,r = -1; t<n;t++){
              int k = (t>r)?0: min(d2[l+r-t], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k-1] == temp[t+k] && isP[t-k-1] && isP[t+k]) k++;
              d2[t] = k--;
              ans += d2[t];
              if(t+k>r) l = t-k-1, r = t+k;
          }

      }
  }

  cout<<ans<<"\n";

  return 0;
}


#include<bits/stdc++.h>
typedef long long ll;
#define F(i,L,R) for (ll i = L; i < R; i++)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define re(x) scanf("%lld",&x)
using namespace std;

ll key = 311, mod = 1e9+7;

int main(){
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  ll n,m, ans = 0;
  cin>>n>>m;
  string a[n+1];
  F(i,1,n+1) cin>>a[i];
  ll cnt[n+1][m+1][26];
  memset(cnt, 0, sizeof(cnt));
  vector<ll> pk(27);
  pk[0] = 1;
  F(i,1,27) pk[i] = (pk[i-1]*key)%mod;
  F(i,1,n+1){
      F(j,1,m+1){
          F(k,0,26) cnt[i][j][k] += cnt[i][j-1][k];
             cnt[i][j][(int)a[i][j-1]-'a']++;
      }
  }
  F(i,1,m+1){
      F(j,i,m+1){
          vector<ll> temp;
          int isP[n];
          F(k,1,n+1){
              ll cntOdd = 0;
              ll h = 0;
              F(l,0,26) {
              	ll diff = (cnt[k][j][l]-cnt[k][i-1][l]);
              	// cout<<l<<" "<<diff<<endl;
              	cntOdd +=  (diff)%2, h = (h+(diff*pk[l])%mod)%mod;
              }
              if(cntOdd>1) isP[k-1] = 0;
              else isP[k-1] = 1;
              temp.pb(h);
          }
          // cout<<i<<" "<<j<<":"<<endl;
          // for(auto z: temp) cout<<z<<" ";
          // 	for(auto a: isP) cout<<a<<" ";
          // cout<<endl;

          vector<int> d1(n), d2(n);

          for(int t = 0, l = 0, r = -1;t<n;t++){
              int k=(t>r)?1:min(d1[l+r-t], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k] == temp[t+k] && isP[t-k] && isP[t+k]) k++;
              d1[t] = k--;
              if(d1[t]>1|| (d1[t] == 1 && isP[t]))ans += d1[t];
              if(t+k>r) l = t-k, r = t+k;
          }

          for(int t = 0, l = 0,r = -1; t<n;t++){
              int k = (t>r)?0: min(d2[l+r-t], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k-1] == temp[t+k] && isP[t-k-1] && isP[t+k]) k++;
              d2[t] = k--;
              ans += d2[t];
              if(t+k>r) l = t-k-1, r = t+k;
          }

      }
  }

  cout<<ans<<"\n";

  return 0;
}


#include<bits/stdc++.h>
typedef long long ll;
#define F(i,L,R) for (ll i = L; i < R; i++)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define re(x) scanf("%lld",&x)
using namespace std;

int main(){
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  ll n,m, ans = 0;
  cin>>n>>m;
  string a[n+1];
  F(i,1,n+1) cin>>a[i];
  ll dp[n+1][m+1][26];
  memset(dp, 0, sizeof(dp));
  F(i,1,n+1){
      F(j,1,m+1){
         F(k,0,26) dp[i][j][k] += dp[i][j-1][k];
         dp[i][j][(int)a[i][j-1]-'a']++;
      }
  }
  F(i,1,m+1){
      F(j,i,m+1){
          vector<string> temp;
          int isP[n];
          F(k,1,n+1){
              string h;
              ll cntOdd = 0;
              F(l,0,26) h += (char)((int)l+'a') + to_string(dp[k][j][l]-dp[k][i-1][l]), cntOdd += (((dp[k][j][l]-dp[k][i-1][l])%2)?1:0);

              if(cntOdd>1) isP[k-1] = 0;
              else isP[k-1] = 1;
              temp.pb(h);
          }

          vector<int> d1(n), d2(n);

          // cout<<i<<" "<<j<<" "<<isP[0]<<endl<<endl;
          for(int t = 0, l = 0, r = -1;t<n;t++){
              int k=(t>r)?1:min(d1[l+r-t], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k] == temp[t+k] && isP[t-k] && isP[t+k]) k++;
              d1[t] = k--;
              // cout<<t<<" "<<d1[t]<<endl;
              if(d1[t]>1|| (d1[t] == 1 && isP[t]))ans += d1[t];
              if(t+k>r) l = t-k, r = t+k;
          }

          for(int t = 0, l = 0,r = -1; t<n;t++){
              int k = (t>r)?0: min(d2[l+r-t], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k-1] == temp[t+k] && isP[t-k-1] && isP[t+k]) k++;
              d2[t] = k--;
              ans += d2[t];
              if(t+k>r) l = t-k-1, r = t+k;
          }

      }
  }

  cout<<ans<<"\n";

  return 0;
}


#include<bits/stdc++.h>
typedef long long ll;
#define F(i,L,R) for (ll i = L; i < R; i++)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define re(x) scanf("%lld",&x)
using namespace std;

ll key = 257, mod = 1e9+7;

int main(){
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  ll n,m, ans = 0;
  cin>>n>>m;
  string a[n+1];
  F(i,1,n+1) cin>>a[i];
  ll dp[n+1][m+1], cnt[n+1][m+1][26];
  memset(dp, 0, sizeof(dp));
  memset(cnt, 0, sizeof(cnt));
  vector<ll> pk(m+1);
  pk[0] = 1;
  F(i,1,n) pk[i] = (pk[i-1]*key)%mod;
  F(i,1,n+1){
      F(j,1,m+1){
          ll val = (int) a[i][j-1] -'a';
          dp[i][j] = (dp[i][j-1] + pk[val]) %mod;
          F(k,0,26) cnt[i][j][k] += cnt[i][j-1][k];
             cnt[i][j][(int)a[i][j-1]-'a']++;
      }
  }
  F(i,1,m+1){
      F(j,i,m+1){
          vector<ll> temp;
          int isP[n];
          F(k,1,n+1){
              ll cntOdd = 0;
              F(l,0,26) cntOdd +=  (cnt[k][j][l]-cnt[k][i-1][l])%2;
              ll h = (dp[k][j] - dp[k][i-1] +mod)%mod;
              if(cntOdd>1) isP[k-1] = 0;
              else isP[k-1] = 1;
              temp.pb(h);
          }
          // cout<<i<<" "<<j<<":"<<endl;
          // for(auto z: temp) cout<<z<<" ";
          	// for(auto a: isP) cout<<a<<" ";
          // cout<<endl;

          vector<int> d1(n), d2(n);

          for(int t = 0, l = 0, r = -1;t<n;t++){
              int k=(t>r)?1:min(d1[l+r-t], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k] == temp[t+k] && isP[t-k] && isP[t+k]) k++;
              d1[t] = k--;
              if(d1[t]>1|| (d1[t] == 1 && isP[t]))ans += d1[t];
              if(t+k>r) l = t-k, r = t+k;
          }

          for(int t = 0, l = 0,r = -1; t<n;t++){
              int k = (t>r)?0: min(d2[l+r-t], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k-1] == temp[t+k] && isP[t-k-1] && isP[t+k]) k++;
              d2[t] = k--;
              ans += d2[t];
              if(t+k>r) l = t-k-1, r = t+k;
          }

      }
  }

  cout<<ans<<"\n";

  return 0;
}


#include<bits/stdc++.h>
typedef long long ll;
#define F(i,L,R) for (ll i = L; i < R; i++)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define re(x) scanf("%lld",&x)
using namespace std;

ll key = 257, mod = 1e9+7;

int main(){
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  ll n,m, ans = 0;
  cin>>n>>m;
  string a[n+1];
  F(i,1,n+1) cin>>a[i];
  ll dp[n+1][m+1], cnt[n+1][m+1][26];
  memset(dp, 0, sizeof(dp));
  memset(cnt, 0, sizeof(cnt));
  vector<ll> pk(m+1);
  pk[0] = 1;
  F(i,1,n) pk[i] = (pk[i-1]*key)%mod;
  F(i,1,n+1){
      F(j,1,m+1){
          ll val = (int) a[i][j-1] -'a';
          dp[i][j] = (dp[i][j-1] + pk[val]) %mod;
          F(k,0,26) cnt[i][j][k] += cnt[i][j-1][k];
             cnt[i][j][(int)a[i][j-1]-'a']++;
      }
  }
  F(i,1,m+1){
      F(j,i,m+1){
          vector<ll> temp;
          int isP[n];
          F(k,1,n+1){
              ll cntOdd = 0;
              F(l,0,26) cntOdd +=  (cnt[k][j][l]-cnt[k][i-1][l])%2;
              ll h = (dp[k][j] - dp[k][i-1] +mod)%mod;
              if(cntOdd>1) isP[k-1] = 0;
              else isP[k-1] = 1;
              temp.pb(h);
          }
          // cout<<i<<" "<<j<<":"<<endl;
          // for(auto z: temp) cout<<z<<" ";
            // for(auto a: isP) cout<<a<<" ";
          // cout<<endl;

          vector<int> d1(n), d2(n);

          for(int t = 0, l = 0, r = -1;t<n;t++){
              int k=(t>r)?1:min(d1[l+r-t], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k] == temp[t+k] && isP[t-k] && isP[t+k]) k++;
              d1[t] = k--;
              if(d1[t]>1|| (d1[t] == 1 && isP[t]))ans += d1[t];
              if(t+k>r) l = t-k, r = t+k;
          }

          for(int t = 0, l = 0,r = -1; t<n;t++){
              int k = (t>r)?0: min(d2[l+r-t+1], r-t+1);
              while(t-k>=0 && t+k<n && temp[t-k-1] == temp[t+k] && isP[t-k-1] && isP[t+k]) k++;
              d2[t] = k--;
              ans += d2[t];
              if(t+k>r) l = t-k-1, r = t+k;
          }

      }
  }

  cout<<ans<<"\n";

  return 0;
}


#include <algorithm>
#include <bitset>
#include <chrono>
#include <complex>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <random>
#include <set>
#include <unordered_map>
#include <unordered_set>

using namespace std;

typedef long long ll;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    ll n, m;
    cin >> n >> m;
    vector<vector<ll>> a(n, vector<ll>(m));
    for (ll i = 0; i < n; i++) {
        for (ll j = 0; j < m; j++) {
            char c;
            cin >> c;
            a[i][j] = c - 'a';
        }
    }
    ll o = 0;
    for (ll l = 0; l < m; l++) {
        vector<vector<ll>> b(n, vector<ll>(26));
        for (ll r = l; r < m; r++) {
            for (ll i = 0; i < n; i++) {
                b[i][a[i][r]]++;
            }
            auto f = [&](ll i) {
                return count_if(b[i].begin(), b[i].end(), [&](ll j) {
                    return j % 2 != 0;
                }) <= 1;
            };
            vector<ll> g(n);
            for (ll i = 0; i < n; i++) {
                if (f(i)) {
                    g[i] = g[i - 1] + 1;
                }
            }
            vector<ll> h(n);
            for (ll i = n - 1; i >= 0; i--) {
                if (f(i)) {
                    h[i] = h[i + 1] + 1;
                }
            }
            vector<ll> x(n);
            for (ll i = 0, l = 0, r = -1; i < n; i++) {
                x[i] = i > r ? 1 : min(x[l + r - i], r - i + 1);
                while (i - x[i] >= 0 && i + x[i] < n && b[i - x[i]] == b[i + x[i]]) {
                    x[i]++;
                }
                if (i + x[i] > r) {
                    l = i - x[i] + 1;
                    r = i + x[i] - 1;
                }
            }
            vector<ll> y(n);
            for (ll i = 0, l = 0, r = -1; i < n; i++) {
                y[i] = i > r ? 0 : min(y[l + r - i + 1], r - i + 1);
                while (i - y[i] - 1 >= 0 && i + y[i] < n && b[i - y[i] - 1] == b[i + y[i]]) {
                    y[i]++;
                }
                if (i + y[i] > r) {
                    l = i - y[i];
                    r = i + y[i] - 1;
                }
            }
            for (ll i = 0; i < n; i++) {
                o += min({x[i], g[i], h[i]});
            }
            for (ll i = 1; i < n; i++) {
                o += min({y[i], g[i - 1], h[i]});
            }
        }
    }
    cout << o << endl;
    return 0;
}


#include <algorithm>
#include <bitset>
#include <chrono>
#include <complex>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <random>
#include <set>
#include <unordered_map>
#include <unordered_set>

using namespace std;

typedef long long ll;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    ll n, m;
    cin >> n >> m;
    vector<vector<ll>> a(n, vector<ll>(m));
    for (ll i = 0; i < n; i++) {
        for (ll j = 0; j < m; j++) {
            char c;
            cin >> c;
            a[i][j] = c - 'a';
        }
    }
    ll o = 0;
    for (ll l = 0; l < m; l++) {
        vector<vector<ll>> b(n, vector<ll>(26));
        for (ll r = l; r < m; r++) {
            for (ll i = 0; i < n; i++) {
                b[i][a[i][r]]++;
            }
            auto f = [&](ll i) {
                return count_if(b[i].begin(), b[i].end(), [&](ll j) {
                    return j % 2 != 0;
                }) <= 1;
            };
            vector<ll> g(n);
            for (ll i = 0; i < n; i++) {
                if (f(i)) {
                    g[i] = (i == 0 ? 0 : g[i - 1]) + 1;
                }
            }
            vector<ll> h(n);
            for (ll i = n - 1; i >= 0; i--) {
                if (f(i)) {
                    h[i] = (i == n - 1 ? 0 : h[i + 1]) + 1;
                }
            }
            vector<ll> x(n);
            for (ll i = 0, l = 0, r = -1; i < n; i++) {
                x[i] = i > r ? 1 : min(x[l + r - i], r - i + 1);
                while (i - x[i] >= 0 && i + x[i] < n && b[i - x[i]] == b[i + x[i]]) {
                    x[i]++;
                }
                if (i + x[i] > r) {
                    l = i - x[i] + 1;
                    r = i + x[i] - 1;
                }
            }
            vector<ll> y(n);
            for (ll i = 0, l = 0, r = -1; i < n; i++) {
                y[i] = i > r ? 0 : min(y[l + r - i + 1], r - i + 1);
                while (i - y[i] - 1 >= 0 && i + y[i] < n && b[i - y[i] - 1] == b[i + y[i]]) {
                    y[i]++;
                }
                if (i + y[i] > r) {
                    l = i - y[i];
                    r = i + y[i] - 1;
                }
            }
            for (ll i = 0; i < n; i++) {
                o += min({x[i], g[i], h[i]});
            }
            for (ll i = 1; i < n; i++) {
                o += min({y[i], g[i - 1], h[i]});
            }
        }
    }
    cout << o << endl;
    return 0;
}