#include<algorithm>
#include<cfloat>
#include<functional>
#include<queue>
#include<vector>

using Vertex = int;
using Flow   = long double;
using Cost   = long double;

const Flow FLOW_INF = LDBL_MAX;
const Cost COST_INF = LDBL_MAX;

struct Edge {
  Vertex from, to;
  Flow capacity;
  Cost cost;
  int rev;
  Edge(Vertex from, Vertex to, Flow capacity = 0, Cost cost = 0, int rev = -1) : from(from), to(to), capacity(capacity), cost(cost), rev(rev) {}
};

class FlowNetwork {
 public:
  FlowNetwork(int n);
  void insert(const Edge&);
  Flow maximum_flow(Vertex source, Vertex sink) const;
  Cost minimum_cost_flow(Vertex source, Vertex sink, Flow f) const;

 private:
  int size_;
  std::vector<std::vector<Edge>> edge_;
};

// コンストラクタ
FlowNetwork::FlowNetwork(int n) : size_(n), edge_(n) {}

// 辺を追加
//   このとき、容量0コスト-cの逆辺も追加する
void FlowNetwork::insert(const Edge& e) {
  edge_.at(e.from).emplace_back(e.from, e.to, e.capacity, e.cost, edge_.at(e.to).size());
  edge_.at(e.to).emplace_back(e.to, e.from, 0, -e.cost, edge_.at(e.from).size() - 1);
}

// 最大流
//   Dinic: O(|V|^2 * |E|)
Flow FlowNetwork::maximum_flow(Vertex source, Vertex sink) const {
  std::vector<int> level;   // 各頂点に対するsourceからの距離
  std::vector<int> itr;     // 各頂点に対して、itr[v]番目以前の辺は使っても無駄
  auto residue = edge_;     // 残余グラフ
  // sourceから各頂点までの距離を計算し、sinkまでの距離を返す
  auto bfs = [&]() {
    level.assign(size_, -1);
    level.at(source) = 0;
    std::queue<int> q;
    q.push(source);
    while(!q.empty()) {
      auto v = q.front();
      q.pop();
      for(const auto& e: residue.at(v)) if(!~level.at(e.to)) if(0 < e.capacity) {
        level.at(e.to) = level.at(e.from) + 1;
        q.push(e.to);
      }
    }
    return level.at(sink);
  };
  // バックトラックを利用してフローを流す
  std::function<Flow(Vertex, Flow)> dfs = [&](Vertex v, Flow cur) {
    if(v == sink) return cur;
    for(auto& i = itr.at(v); i < residue.at(v).size(); ++i) {
      auto& e = residue.at(v).at(i);
      if(level.at(e.from) < level.at(e.to)) if(0 < e.capacity) {
        auto f = dfs(e.to, std::min(cur, e.capacity));
        if(f == 0) continue;
        e.capacity -= f;
        residue.at(e.to).at(e.rev).capacity += f;
        return f;
      }
    }
    return Flow(0);
  };
  // Dinic
  Flow result = 0;
  while(~bfs()) {
    itr.assign(size_, 0);
    while(auto f = dfs(source, FLOW_INF)) result += f;
  }
  return result;
}

#include<bits/stdc++.h>
using namespace std;

void ng() {
  cout << -1 << endl;
  exit(0);
}

int main() {
  int H, W;
  cin >> H >> W;
  vector<string> S(H);
  for(auto& i: S) cin >> i;

  FlowNetwork G(H + W + 2);
  auto SOURCE = H+W, SINK = H+W+1;

  for(auto i=0; i<H; ++i) for(auto j=0; j<W; ++j) {
    if(S[i][j]=='S') {
      G.insert({SOURCE, i, FLOW_INF, 0});
      G.insert({SOURCE, H+j, FLOW_INF, 0});
    }
    if(S[i][j]=='T') {
      G.insert({i, SINK, FLOW_INF, 0});
      G.insert({H+j, SINK, FLOW_INF, 0});
    }
    if(S[i][j]=='o') {
      G.insert({i, H+j, 1, 1});
      G.insert({H+j, i, 1, 1});
    }
    if(S[i][j]=='S' || S[i][j]=='T') {
      for(auto y=i+1; y<H; ++y) if(S[y][j]=='S' || S[y][j]=='T') ng();
      for(auto x=j+1; x<W; ++x) if(S[i][x]=='S' || S[i][x]=='T') ng();
    }
  }

  cout << G.maximum_flow(SOURCE, SINK) << endl;
}


Measured memory_bytes: 6016