#include <bits/stdc++.h>
using namespace std;
struct fast_ios {
  fast_ios() {
    cin.tie(nullptr), ios::sync_with_stdio(false),
        cout << fixed << setprecision(20);
  };
} fast_ios_;
template <class T>
auto add = [](T a, T b) -> T { return a + b; };
template <class T>
auto f_max = [](T a, T b) -> T { return max(a, b); };
template <class T>
auto f_min = [](T a, T b) -> T { return min(a, b); };
template <class T>
using V = vector<T>;
using Vl = V<int>;
using VVl = V<Vl>;
template <typename T>
ostream& operator<<(ostream& os, const vector<T>& v) {
  for (int i = 0; i < (int)v.size(); i++)
    os << v[i] << (i + 1 != v.size() ? " " : "");
  return os;
}
template <typename T>
istream& operator>>(istream& is, vector<T>& v) {
  for (T& in : v) is >> in;
  return is;
}
template <class T>
bool chmax(T& a, const T& b) {
  if (a < b) {
    a = b;
    return 1;
  }
  return 0;
}
template <class T>
bool chmin(T& a, const T& b) {
  if (b < a) {
    a = b;
    return 1;
  }
  return 0;
}
int gcd(int a, int b) {
  if (b == 0)
    return a;
  else
    return gcd(b, a % b);
}
int ceil(int a, int b) { return (a + b - 1) / b; }
int digit(int a) { return (int)log10(a); }
int e_dist(pair<int, int> a, pair<int, int> b) {
  return abs(a.first - b.first) * abs(a.first - b.first) +
         abs(a.second - b.second) * abs(a.second - b.second);
}
int m_dist(pair<int, int> a, pair<int, int> b) {
  return abs(a.first - b.first) + abs(a.second - b.second);
}
bool check_overflow(int a, int b, int limit) { return a > limit / b; }
void Worshall_Floyd(VVl& g) {
  for (int k = (0), k_end_ = (((int)(g).size())); k < k_end_; ++k)
    for (int i = (0), i_end_ = (((int)(g).size())); i < i_end_; ++i)
      for (int j = (0), j_end_ = (((int)(g).size())); j < j_end_; ++j)
        chmin(g[i][j], g[i][k] + g[k][j]);
}
const int MOD1000000007 = 1000000007, MOD998244353 = 998244353, INF = 5e18;
int dx[8] = {1, 0, -1, 0, 1, -1, 1, -1}, dy[8] = {0, 1, 0, -1, -1, -1, 1, 1};
bool YN(bool flag) {
  cout << (flag ? "YES" : "NO") << '\n';
  return flag;
}
bool yn(bool flag) {
  cout << (flag ? "Yes" : "No") << '\n';
  return flag;
}
struct Edge {
  int from, to;
  int cost;
  Edge() {}
  Edge(int u, int v, int c) {
    cost = c;
    from = u;
    to = v;
  }
  bool operator<(const Edge& e) const { return cost < e.cost; }
};
struct WeightedEdge {
  int to;
  int cost;
  WeightedEdge(int v, int c = 1) {
    to = v;
    cost = c;
  }
  bool operator<(const WeightedEdge& e) const { return cost < e.cost; }
};
using WeightedGraph = V<V<WeightedEdge>>;
struct UnionFind {
  vector<int> data;
  UnionFind(int sz) { data.assign(sz, -1); }
  bool unite(int x, int y) {
    x = find(x), y = find(y);
    if (x == y) return (false);
    if (data[x] > data[y]) swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return (true);
  }
  int find(int k) {
    if (data[k] < 0) return (k);
    return (data[k] = find(data[k]));
  }
  int size(int k) { return (-data[find(k)]); }
};
int N, M;
int main() {
  cin >> N >> M;
  string input;
  cin >> input;
  int cnt = 0;
  for (int j = (0), j_end_ = (M / 4); j < j_end_; ++j) {
    int curr;
    if (isalpha(input[j])) {
      curr = input[j] - 'A' + 10;
    } else {
      curr = input[j] - '0';
    }
    while (curr != 0) {
      if (curr % 2 == 1) cnt++;
      curr /= 2;
    }
  }
  Vl group(M);
  Vl left(M * 2);
  string prv;
  {
    UnionFind tree(M);
    string s = "";
    for (int j = (0), j_end_ = (M / 4); j < j_end_; ++j) {
      int curr;
      if (isalpha(input[j])) {
        curr = input[j] - 'A' + 10;
      } else {
        curr = input[j] - '0';
      }
      for (int k = (4) - 1, k_begin_ = (0); k >= k_begin_; --k) {
        s += (char)('0' + (curr >> k) % 2);
      }
    }
    for (int i = (0), i_end_ = (((int)(s).size()) - 1); i < i_end_; ++i)
      if (s[i] == '1' && s[i + 1] == '1') {
        cnt--;
        tree.unite(i, i + 1);
      }
    for (int j = (0), j_end_ = (M); j < j_end_; ++j) {
      group[j] = tree.find(j);
    }
    prv = s;
  }
  for (int i = (0), i_end_ = (N - 1); i < i_end_; ++i) {
    cin >> input;
    for (int j = (0), j_end_ = (M / 4); j < j_end_; ++j) {
      int curr;
      if (isalpha(input[j])) {
        curr = input[j] - 'A' + 10;
      } else {
        curr = input[j] - '0';
      }
      while (curr != 0) {
        if (curr % 2 == 1) cnt++;
        curr /= 2;
      }
    }
    UnionFind tree(2 * M);
    for (int j = (0), j_end_ = (M); j < j_end_; ++j) {
      left[group[j]] = j;
    }
    for (int j = (0), j_end_ = (M); j < j_end_; ++j) {
      tree.unite(j, left[group[j]]);
    }
    for (int j = (0), j_end_ = (M); j < j_end_; ++j) group[j] = 0;
    for (int j = (0), j_end_ = (M * 2); j < j_end_; ++j) left[j] = 0;
    string nxt = "";
    for (int j = (0), j_end_ = (M / 4); j < j_end_; ++j) {
      int curr;
      if (isalpha(input[j])) {
        curr = input[j] - 'A' + 10;
      } else {
        curr = input[j] - '0';
      }
      for (int k = (4) - 1, k_begin_ = (0); k >= k_begin_; --k) {
        nxt += (char)('0' + (curr >> k) % 2);
      }
    }
    for (int j = (0), j_end_ = (M - 1); j < j_end_; ++j) {
      if (nxt[j] == '1' && nxt[j + 1] == '1') {
        cnt--;
        tree.unite(M + j, M + j + 1);
      }
    }
    for (int j = (0), j_end_ = (M); j < j_end_; ++j) {
      if (prv[j] == '1' && nxt[j] == '1') {
        if (tree.find(j) != tree.find(j + M)) {
          cnt--;
          tree.unite(j, j + M);
        }
      }
    }
    for (int j = (0), j_end_ = (M); j < j_end_; ++j) {
      group[j] = tree.find(j + M);
    }
    prv = nxt;
  }
  cout << cnt << '\n';
}
