#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <bitset>
#include <array>
#include <vector>
#include <queue>
#include <set>
#include <cmath> // 変数名にy1が使えなくなるかも…。
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <limits>
#include <string>
#include <cstring>

const int INF = std::numeric_limits<int>::max();

class Max_Flow { // 最大フローをDinicで解くクラス
public:
    static const int MAX_V = 20010;
    struct edge { int to, cap, rev; };
    std::vector<edge> G[MAX_V]; // グラフの隣接リスト表現
    int level[MAX_V]; // sからの距離
    int iter[MAX_V]; // vの各辺について、次に調べる辺の番号がiter[v]

    Max_Flow();

    // fromからtoへ向かう容量capの辺をグラフに追加する
    // 容量0の逆辺も追加する。
    void add_edge(const int from, const int to, const int cap);

    // sからの最短距離をBFSで計算し、levelに代入する。
    void bfs(const int s);

    // 増加パスをDFSで探す。v 現在地, t 終点, f 現状流せる最大流
    // 再帰的に呼び出す関数で、dfs(s, t, INF)の形式で利用すると、増加パスの増分を返す。
    int dfs(const int v, const int t, const int f);

    // sからtへの最大流を求める。
    int solve(const int s, const int t);
};

Max_Flow::Max_Flow(){}

// fromからtoへ向かう容量capの辺をグラフに追加する
// 容量0の逆辺も追加する。
void Max_Flow::add_edge(const int from, const int to, const int cap) {
    G[from].push_back((edge){to, cap, (int)G[to].size()});
    G[to].push_back((edge){from, 0, (int)G[from].size() - 1});
}

// sからの最短距離をBFSで計算し、levelに代入する。
void Max_Flow::bfs(const int s) {
    //std::memset(level, INF, sizeof(level)); // 何故かmemsetだと-1が代入されてしまう。
    std::fill(level, level + MAX_V, INF);
    std::queue<int> que;
    level[s] = 0;
    que.push(s);
    while(!que.empty()) {
        const int v = que.front(); que.pop();
        for (int i = 0; i < G[v].size(); i++) {
            const edge& e = G[v][i];
            if (e.cap > 0 && level[e.to] == INF) {
                level[e.to] = level[v] + 1;
                que.push(e.to);
            }
        }
    }
}

// 増加パスをDFSで探す。v 現在地, t 終点, f 現状流せる最大流
int Max_Flow::dfs(const int v, const int t, const int f) {
    if (v == t) return f;
    for (int &i = iter[v]; i < G[v].size(); i++) {
        edge &e = G[v][i];
        if (e.cap > 0 && level[v] < level[e.to]) { // 距離が増加するパスだけを対象にする。
            const int d = dfs(e.to, t, std::min(f, e.cap));
            if (d > 0) {
                e.cap -= d;
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}

// sからtへの最大流を求める。
int Max_Flow::solve(const int s, const int t) {
    int flow = 0;
    while(true) {
        bfs(s);
        if (level[t] == INF) {
            return flow;
        }
        std::memset(iter, 0, sizeof(iter));
        int f;
        while((f = dfs(s, t, INF)) > 0) { 
            flow += f;
        }
        //距離増加パスに流せるだけ流せたら、最短の増加パスの長さは真に増加する。
    }
}

class Directed_Graph {
    public:
    struct Edge { 
        int to; int64_t cost;
    };
    typedef std::vector<std::vector<Edge>> adj_list;
    adj_list G;
    int node_num;
    Directed_Graph() {}
    Directed_Graph(const int node_num_in) {
        node_num = node_num_in;
        G.resize(node_num);
    }
    void add_edge(const int from, const int to, const long long cost) {
        Edge edge; edge.to = to; edge.cost = cost;
        G[from].push_back(edge);
    }
    void add_edge_both(const int node1, const int node2, const long long cost) {
        add_edge(node1, node2, cost);
        add_edge(node2, node1, cost);
    }

    // 点sからの最短距離の配列を返す。
    // 戻りの値としてint64_tを用い、初期値としてint64_tの最大値を用いるのは、dist[v] + edge.costの計算でのオーバーフローを防ぐため。
    // 正しい最短距離がint64_tの最大値を超えるような事態は想定していないので、最終的にdist[v] == std::numeric_limits<int64_t>::max()だった場合、vには到達できないことを意味する。
    std::vector<int64_t> Dijkstra(int s) {
        std::vector<int64_t> dist;
        dist.resize(node_num, std::numeric_limits<int64_t>::max());

        typedef std::pair<int64_t, int> Pair;
        std::priority_queue <Pair, std::vector<Pair>, std::greater<Pair>> que;
        dist[s] = 0;
        que.push(Pair(0, s));

        while (!que.empty()) {
            Pair pair = que.top(); que.pop();
            int v = pair.second;
            if (dist[v] < pair.first) { continue; }
            for (Edge edge : G[v]) {
                if (dist[v] + edge.cost < dist[edge.to]) {
                    dist[edge.to] = dist[v] + edge.cost;
                    que.push(Pair(dist[edge.to], edge.to));
                }
            }
        }
        return dist;
    }

    std::vector<std::vector<int64_t>> Warshall_Floyd(const long long dist_inf) {
        std::vector<std::vector<int64_t>> dist; dist.resize(node_num);
        for (int i = 0; i < dist.size(); i++) {
            dist[i].resize(node_num);
            for (int j = 0; j < dist[i].size(); j++) {
                dist[i][j] = dist_inf;
            }
            dist[i][i] = 0;
            for (auto e : G[i]) {
                dist[i][e.to] = std::min(dist[i][e.to], e.cost);
            }
        }
        for (int k = 0; k < node_num; k++) {
            for (int i = 0; i < node_num; i++) {
                for (int j = 0; j < node_num; j++) {
                    dist[i][j] = std::min(dist[i][j], dist[i][k] + dist[k][j]);
                }
            }
        }
        return dist;
    }

    bool bipartite_search(const int node, const int c, std::vector<int>& ret) {
        assert(c == 1 || c == -1); assert(node < ret.size());
        if (ret[node] == -c) { return false; }
        if (ret[node] == c) { return true; }
        assert(ret[node] == 0);
        ret[node] = c;
        for (Edge e : G[node]) {
            if (!bipartite_search(e.to, -c, ret)) { return false; }
        }
        return true;
    }

    std::pair<bool, std::vector<int>> is_bipartite(const int begin, const int end) {
        std::vector<int> ret; ret.resize(end+1, 0);
        for (int i = begin; i <= end; i++) {
            if (ret[i] == 0 && !bipartite_search(i, 1, ret)) {
                return std::pair<bool, std::vector<int>>({false, ret});
            }
        }
        return std::pair<bool, std::vector<int>>({true, ret});
    }

    std::pair<adj_list, std::vector<std::vector<int>>> auxilarity_network() const {
        adj_list Net;
        std::vector<std::vector<int>> Rev;
        Net.resize(node_num);
        Rev.resize(node_num);
        for (int i = 0; i < node_num; i++) {
            for (Edge e : G[i]) {
                Net[i].push_back(e);
                Rev[i].push_back(-1);
            }
        }
        for (int i = 0; i < node_num; i++) {
            for (int j = 0; j < G[i].size(); j++) {
                Edge r; r.to = i; r.cost = 0;
                Rev[i][j] = Net[G[i][j].to].size();
                Net[G[i][j].to].push_back(r);
                Rev[G[i][j].to].push_back(j);
            }
        }
        return std::pair<adj_list, std::vector<std::vector<int>>>({Net, Rev});
    }

    int64_t dfs_increase_path(const int v, const int t, const int64_t f, std::vector<bool>& used, adj_list& Net, const std::vector<std::vector<int>>& Rev) const {
        if (v == t) { return f; }
        used[v] = true;
        for (int i = 0; i < Net[v].size(); i++) {
            Edge &e = Net[v][i];
            if (!used[e.to] && 0 < e.cost) {
                //std::cout << v << " " << e.to << std::endl;
                int d = dfs_increase_path(e.to, t, std::min(f, e.cost), used, Net, Rev);
                //std::cout << v << " " << e.to << " " << d << " " << std::endl;
                if (0 < d) {
                    e.cost -= d;
                    Net[e.to][Rev[v][i]].cost += d;
                    return d;
                }
            }
        }
        return 0;
    }

    // 補助ネットワークから元のグラフのフローを出力する関数。
    adj_list aux_to_flow(const adj_list& Net) const {
        adj_list ret; ret.resize(node_num);
        for (int i = 0; i < node_num; i++) {
            for (int j = 0; j < G[i].size(); j++) {
                ret[i].push_back(G[i][j]);
                ret[i][j].cost -= Net[i][j].cost;
            }
        }
        return ret;
    }

    std::pair<int64_t, adj_list> max_flow(const int s, const int t) const {
        auto aux = auxilarity_network();
        int64_t flow = 0;
        while (true) {
            std::vector<bool> used; used.resize(node_num, false);
            int64_t f = dfs_increase_path(s, t, std::numeric_limits<int64_t>::max(), used, aux.first, aux.second);
            if (f == 0) { break; }
            flow += f;
        }
        /*
        adj_list ret; ret.resize(node_num);
        for (int i = 0; i < node_num; i++) {
            for (int j = 0; j < G[i].size(); j++) {
                ret[i].push_back(G[i][j]);
                ret[i][j].cost -= aux.first[i][j].cost;
            }
        }
        return std::pair<int64_t, adj_list>({ flow, ret });
        */
        return std::pair<int64_t, adj_list>({ flow, aux_to_flow(aux.first) });
    }

    // sからの最短距離をBFSで計算する。ここでの最短距離は、Netの容量1以上の辺数のことであり、辺の容量には依存しない。
    std::vector<int> bfs_level(const int s, const adj_list& Net) const {
        std::vector<int> level; level.resize(node_num, -1);
        std::queue<int> que;
        level[s] = 0;
        que.push(s);
        while(!que.empty()) {
            const int v = que.front(); que.pop();
            for (int i = 0; i < Net[v].size(); i++) {
                const Edge& e = Net[v][i];
                if (0 < e.cost && level[e.to] == -1) { // 全ての辺は距離1なので、BFSでは1つの点は1度だけpushすればよい。
                    level[e.to] = level[v] + 1;
                    que.push(e.to);
                }
            }
        }
        return level;
    }

    // 増加パスをDFSで探す。v 現在地, t 終点, f 現状流せる最大流
    int64_t dfs_increase_path(const int v, const int t, const int64_t f, std::vector<int>& iter, adj_list& Net, const std::vector<std::vector<int>>& Rev, const std::vector<int>& level) const {
        if (v == t) return f;
        for (int &i = iter[v]; i < Net[v].size(); i++) {
            Edge &e = Net[v][i];
            if (0 < e.cost && level[v] < level[e.to]) { // 距離が増加するパスだけを対象にする。
                const int64_t d = dfs_increase_path(e.to, t, std::min(f, e.cost), iter, Net, Rev, level);
                if (d > 0) {
                    e.cost -= d;
                    Net[e.to][Rev[v][i]].cost += d;
                    return d;
                }
            }
        }
        return 0;
    }

    std::pair<int64_t, adj_list> max_flowD(const int s, const int t) const {
        auto aux = auxilarity_network();
        int64_t flow = 0;
        while (true) {
            std::vector<int> level = bfs_level(s, aux.first);
            if (level[t] == -1) { break; }
            std::vector<int> iter; iter.resize(node_num, 0);
            int64_t f;
            while ((f = dfs_increase_path(s, t, std::numeric_limits<int64_t>::max(), iter, aux.first, aux.second, level)) > 0) {
                flow += f;
            }
        }
        return std::pair<int64_t, adj_list>({ flow, aux_to_flow(aux.first) });
    }
};

const int MAX_N = 110;
int H, W;
char a[MAX_N][MAX_N];

Directed_Graph dg = Directed_Graph(MAX_N * MAX_N * 2);
//Max_Flow dg;

int main(int argc, char **argv) {
    std::cin >> H >> W;

    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W; j++) {
            std::cin >> a[i][j];
        }
    }

    //std::cout << "check1" << std::endl;

    int si, sj, ti, tj;

    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W; j++) {
            if (a[i][j] == 'o') {
                dg.add_edge(i*W + j, i*W + j + H*W, 1);
            } else if (a[i][j] == 'S') {
                si = i; sj = j;
            } else if (a[i][j] == 'T') {
                ti = i; tj = j;
            }
        }
    }

    if (si == ti || sj == tj) {
        std::cout << -1 << std::endl;
        return 0;
    }

    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W; j++) {
            for (int k = 0; k < W; k++) {
                if (j != k && (a[i][j] == 'o' || a[i][j] == 'S') && (a[i][k] == 'o' || a[i][k] == 'T')) {
                    dg.add_edge(i*W + j + H*W, i*W + k, 1);
                }
            }
        }
    }

    for (int i = 0; i < W; i++) {
        for (int j = 0; j < H; j++) {
            for (int k = 0; k < H; k++) {
                if (j != k && (a[j][i] == 'o' || a[j][i] == 'S') && (a[k][i] == 'o' || a[k][i] == 'T')) {
                    dg.add_edge(j*W + i + H*W, k*W + i, 1);
                }
            }
        }
    }

    auto ret = dg.max_flowD(si*W + sj + H*W, ti*W + tj);

    std::cout << ret.first << std::endl;

    //std::cout << dg.solve(si*W + sj + H*W, ti*W + tj) << std::endl;

    return 0;
}


Measured memory_bytes: 224896