#include <bits/stdc++.h>
using namespace std;
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }


#define COUT(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << endl
template<class T1, class T2> ostream& operator << (ostream &s, pair<T1,T2> P)
{ return s << '<' << P.first << ", " << P.second << '>'; }
template<class T> ostream& operator << (ostream &s, vector<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, vector<vector<T> > P)
{ for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; }
template<class T> ostream& operator << (ostream &s, set<T> P)
{ for(auto it : P) { s << "<" << it << "> "; } return s << endl; }
template<class T1, class T2> ostream& operator << (ostream &s, map<T1,T2> P)
{ for(auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s << endl; }



template<int MOD> struct Fp {
    long long val;
    constexpr Fp(long long v = 0) noexcept : val(v % MOD) {
        if (val < 0) val += MOD;
    }
    constexpr int getmod() { return MOD; }
    constexpr Fp operator - () const noexcept {
        return val ? MOD - val : 0;
    }
    constexpr Fp operator + (const Fp& r) const noexcept { return Fp(*this) += r; }
    constexpr Fp operator - (const Fp& r) const noexcept { return Fp(*this) -= r; }
    constexpr Fp operator * (const Fp& r) const noexcept { return Fp(*this) *= r; }
    constexpr Fp operator / (const Fp& r) const noexcept { return Fp(*this) /= r; }
    constexpr Fp& operator += (const Fp& r) noexcept {
        val += r.val;
        if (val >= MOD) val -= MOD;
        return *this;
    }
    constexpr Fp& operator -= (const Fp& r) noexcept {
        val -= r.val;
        if (val < 0) val += MOD;
        return *this;
    }
    constexpr Fp& operator *= (const Fp& r) noexcept {
        val = val * r.val % MOD;
        return *this;
    }
    constexpr Fp& operator /= (const Fp& r) noexcept {
        long long a = r.val, b = MOD, u = 1, v = 0;
        while (b) {
            long long t = a / b;
            a -= t * b; swap(a, b);
            u -= t * v; swap(u, v);
        }
        val = val * u % MOD;
        if (val < 0) val += MOD;
        return *this;
    }
    constexpr bool operator == (const Fp& r) const noexcept {
        return this->val == r.val;
    }
    constexpr bool operator != (const Fp& r) const noexcept {
        return this->val != r.val;
    }
    friend constexpr ostream& operator << (ostream &os, const Fp<MOD>& x) noexcept {
        return os << x.val;
    }
    friend constexpr Fp<MOD> modpow(const Fp<MOD> &a, long long n) noexcept {
        if (n == 0) return 1;
        auto t = modpow(a, n / 2);
        t = t * t;
        if (n & 1) t = t * a;
        return t;
    }
};

// 二項係数ライブラリ
template<class T> struct BiCoef {
    vector<T> fact_, inv_, finv_;
    constexpr BiCoef() {}
    constexpr BiCoef(int n) noexcept : fact_(n, 1), inv_(n, 1), finv_(n, 1) {
        init(n);
    }
    constexpr void init(int n) noexcept {
        fact_.assign(n, 1), inv_.assign(n, 1), finv_.assign(n, 1);
        int MOD = fact_[0].getmod();
        for(int i = 2; i < n; i++){
            fact_[i] = fact_[i-1] * i;
            inv_[i] = -inv_[MOD%i] * (MOD/i);
            finv_[i] = finv_[i-1] * inv_[i];
        }
    }
    constexpr T com(int n, int k) const noexcept {
        if (n < k || n < 0 || k < 0) return 0;
        return fact_[n] * finv_[k] * finv_[n-k];
    }
    constexpr T fact(int n) const noexcept {
        if (n < 0) return 0;
        return fact_[n];
    }
    constexpr T inv(int n) const noexcept {
        if (n < 0) return 0;
        return inv_[n];
    }
    constexpr T finv(int n) const noexcept {
        if (n < 0) return 0;
        return finv_[n];
    }
};

const int MOD = 998244353;
using mint = Fp<MOD>;
BiCoef<mint> bc;

int H, W;
vector<string> A;

int main() {
    bc.init(110);

    // 長方形
    vector<vector<mint>> all(15, vector<mint>(15, 0));
    auto bein = all;
    auto notin = all;
    for (int i = 0; i < 15; ++i) all[0][i] = all[i][0] = modpow(mint(2), i);
    for (int a = 1; a < 15; ++a) {
        for (int b = 1; b < 15; ++b) {
            notin[a][b] = 1;
            for (int i = 1; i < a; ++i) {
                for (int j = 1; j < b; ++j) {
                    notin[a][b] += notin[a-i][b-j] * bc.com(a, i) * bc.com(b, j);
                }
            }
        }
    }
    for (int a = 1; a < 15; ++a) {
        for (int b = 1; b < 15; ++b) {
            bein[a][b] = 1;
            for (int i = 0; i < a; ++i) {
                for (int j = 0; j < b; ++j) {
                    if (i + j == 0) continue;
                    bein[a][b] += notin[a-i][b-j] * bc.com(a, i) * bc.com(b, j);
                }
            }
            all[a][b] = notin[a][b] + bein[a][b];
        }
    }

    // はみ出しあり
    vector<vector<mint>> subsub(15, vector<mint>(15, 0));
    auto sub = subsub, part = subsub;
    for (int i = 0; i < 15; ++i) {
        subsub[1][i] = 1;
        subsub[i][1] = modpow(mint(2), i) - 1;
    }
    for (int s = 2; s < 30; ++s) {
        for (int a = 1; a < 15; ++a) {
            int b = s - a;
            if (b < 1 || b >= 15) continue;
            subsub[a][b] = 1;
            for (int i = 1; i < a; ++i) {
                subsub[a][b] += bc.com(a, i) * subsub[b][a-i];
            }
        }
    }
    for (int a = 0; a < 15; ++a) {
        for (int b = 0; b < 15; ++b) {
            if (a == 0) sub[a][b] = 1;
            else if (b == 0) sub[a][b] = 1;
            else sub[a][b] = subsub[a][b] + subsub[b][a];
        }
    }
    for (int i = 0; i < 15; ++i) part[0][i] = part[i][0] = modpow(mint(2), i);
    for (int a = 1; a < 14; ++a) {
        for (int b = 1; b < 14; ++b) {
            for (int i = 0; i <= a; ++i) {
                for (int j = 0; j <= b; ++j) {
                    part[a][b] += bc.com(a, i) * bc.com(b, j) *
                        sub[i][j] * sub[a-i][b-j];
                }
            }
        }
    }

    cin >> H >> W;
    A.resize(H);
    for (int h = 0; h < H; ++h) cin >> A[h];

    mint res = all[H][W];
    for (int bit = 0; bit < (1<<H)-1; ++bit) {
        vector<int> tate;
        for (int i = 0; i < H; ++i) if (!(bit & (1<<i))) tate.push_back(i);
        for (int bit2 = 0; bit2 < (1<<W)-1; ++bit2) {
            vector<int> yoko;
            for (int i = 0; i < W; ++i) if (!(bit2 & (1<<i))) yoko.push_back(i);

            bool ok = true;
            for (auto i : tate) {
                set<char> se;
                for (auto j : yoko) se.insert(A[i][j]);
                if (se.size() != 2) ok = false;
            }
            for (auto j : yoko) {
                set<char> se;
                for (auto i : tate) se.insert(A[i][j]);
                if (se.size() != 2) ok = false;
            }

            if (ok) {
                int c = __builtin_popcount(bit);
                int r = __builtin_popcount(bit2);
                mint add = part[c][r];
                res += add;
            }
        }
    }
    cout << res << endl;
}


Measured memory_bytes: 256