#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 7;
const int inf = 0x3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-8;
const double PI = acos(-1);
template <class T, class S>
inline void add(T& a, S b) {
  a += b;
  if (a >= mod) a -= mod;
}
template <class T, class S>
inline void sub(T& a, S b) {
  a -= b;
  if (a < 0) a += mod;
}
template <class T, class S>
inline bool chkmax(T& a, S b) {
  return a < b ? a = b, true : false;
}
template <class T, class S>
inline bool chkmin(T& a, S b) {
  return a > b ? a = b, true : false;
}
bitset<1 << 17> can[17][17];
char s[N];
int n, p, ans = inf, c[1 << 17];
vector<int> pos[17], vc;
vector<int> to[1 << 17];
bool G[17][17];
bool ok[1 << 17];
bool vis[1 << 17];
struct ST {
  int Log[N], dp[N][20];
  void build(char* s, int n) {
    Log[1] = 0;
    for (int i = 2; i <= n; i++) Log[i] = Log[i >> 1] + 1;
    for (int i = 1; i <= n; i++) dp[i][0] = 1 << (s[i] - 'a');
    for (int j = 1; j <= Log[n]; j++)
      for (int i = 1; i <= n; i++)
        dp[i][j] = dp[i][j - 1] | dp[i + (1 << (j - 1))][j - 1];
  }
  inline int query(int L, int R) {
    if (L > R) return 0;
    int k = Log[R - L + 1];
    return dp[L][k] | dp[R - (1 << k) + 1][k];
  }
} rmqOr;
int main() {
  scanf("%d%d", &n, &p);
  scanf("%s", s + 1);
  for (int i = 0; i < p; i++)
    for (int j = 0; j < p; j++) scanf("%d", &G[i][j]);
  for (int i = 0; i < (1 << p); i++) {
    ok[i] = true;
    for (int j = 0; j < p; j++) {
      if (i >> j & 1) continue;
      to[i].push_back(j);
    }
  }
  rmqOr.build(s, n);
  for (int i = 1; i <= n; i++) pos[s[i] - 'a'].push_back(i);
  for (int i = 1; i < (1 << p); i++)
    for (int j = 0; j < p; j++)
      if (i >> j & 1) c[i] += ((int)pos[j].size());
  for (int i = 0; i < p; i++) {
    for (int j = 0; j < p; j++) {
      if (G[i][j]) continue;
      if (i == j) {
        for (int k = 1; k < ((int)pos[i].size()); k++)
          can[i][j][rmqOr.query(pos[i][k - 1] + 1, pos[i][k] - 1)] = true;
      } else {
        vc.resize(((int)pos[i].size()) + ((int)pos[j].size()));
        merge((pos[i]).begin(), (pos[i]).end(), (pos[j]).begin(),
              (pos[j]).end(), vc.begin());
        for (int k = 1; k < ((int)vc.size()); k++) {
          if (s[vc[k]] != s[vc[k - 1]]) {
            can[i][j][rmqOr.query(vc[k - 1] + 1, vc[k] - 1)] = true;
          }
        }
      }
      for (int mask = 0; mask < (1 << p); mask++) {
        if (!can[i][j][mask]) continue;
        for (auto& k : to[mask]) can[i][j][mask | (1 << k)] = true;
      }
    }
  }
  for (int mask = 0; mask < (1 << p); mask++) {
    int fmask = mask ^ ((1 << p) - 1);
    for (int i = 0; i < p && ok[mask]; i++) {
      if (mask >> i & 1) {
        for (int j = i; j < p && ok[mask]; j++) {
          if (mask >> j & 1) {
            if (can[i][j][fmask]) ok[mask] = false;
          }
        }
      }
    }
  }
  vis[(1 << p) - 1] = true;
  queue<int> que;
  que.push((1 << p) - 1);
  while (!que.empty()) {
    int mask = que.front();
    que.pop();
    chkmin(ans, c[mask]);
    for (int i = 0; i < p; i++) {
      if (mask >> i & 1 && !vis[mask ^ (1 << i)] && ok[mask ^ (1 << i)]) {
        vis[mask ^ (1 << i)] = true;
        que.push(mask ^ (1 << i));
      }
    }
  }
  printf("%d\n", ans);
  return 0;
}
