#include <bits/stdc++.h>
using namespace std;
template <class T>
using vv = vector<vector<T>>;
template <class T>
inline bool MX(T &l, const T &r) {
  return l < r ? l = r, 1 : 0;
}
template <class T>
inline bool MN(T &l, const T &r) {
  return l > r ? l = r, 1 : 0;
}
const long long MOD = 1e9 + 7;
int N;
int M;
int D;
int S[114514][51];
bool used[114514];
int least[114514];
bool reach[114514][50];
bool can[114514][50];
bool ms[114514][50];
int belong[114514];
int opened[114514][50];
int dp[114514][50];
int wei[114514][50];
vector<pair<int, int>> des[114514];
vector<int> es[114514];
vector<int> res[114514];
vector<int> ds[114514];
vector<int> vs;
void Dfs(int v) {
  used[v] = true;
  for (auto &u : es[v]) {
    if (used[u]) continue;
    Dfs(u);
  }
  vs.emplace_back(v);
}
void Rdfs(int v, int grp) {
  belong[v] = grp;
  used[v] = true;
  ds[grp].emplace_back(v);
  for (auto &u : res[v]) {
    if (used[u]) continue;
    Rdfs(u, grp);
  }
}
int main() {
  scanf("%d%d%d", &N, &M, &D);
  for (int(i) = 0; (i) < (M); ++(i)) {
    int u, v;
    scanf("%d%d", &u, &v);
    --u;
    --v;
    es[u].emplace_back(v);
    res[v].emplace_back(u);
  }
  for (int(v) = 0; (v) < (N); ++(v)) {
    least[v] = -1;
    for (int(d) = 0; (d) < (D); ++(d)) {
      scanf("%01d", &opened[v][d]);
    }
  }
  for (int(v) = 0; (v) < (N); ++(v)) {
    if (!used[v]) Dfs(v);
  }
  reverse((vs).begin(), (vs).end());
  fill(used, used + N, false);
  int grp = 0;
  for (int v : vs) {
    if (!used[v]) Rdfs(v, grp++);
  }
  for (int(g) = (grp)-1; (g) >= 0; --(g)) {
    int s = ds[g][0];
    can[s][0] = true;
    queue<pair<int, int>> que;
    que.push(pair<int, int>(s, 0));
    while (!que.empty()) {
      int v, d;
      tie(v, d) = que.front();
      que.pop();
      int nd = (d + 1) % D;
      for (int u : es[v]) {
        if (belong[u] != g) {
          assert(least[u] != -1);
          int nnd = (nd + D - least[u]) % D;
          int h = belong[u];
          des[g].emplace_back(pair<int, int>(h, nnd));
        } else if (!can[u][nd]) {
          can[u][nd] = true;
          que.push(pair<int, int>(u, nd));
        }
      }
    }
    int w = D;
    for (int(i) = 1; (i) < (D); ++(i)) {
      if (can[s][i]) {
        w = i;
        break;
      }
    }
    for (int(i) = 0; (i) < (D); ++(i)) {
      assert(can[s][i] == (i % w == 0));
    }
    for (int v : ds[g]) {
      least[v] = D;
      for (int(d) = 0; (d) < (D); ++(d)) {
        if (can[v][d]) MN(least[v], d);
      }
      for (int(d) = 0; (d) < (D); ++(d)) {
        int nd = (d + least[v]) % D;
        ms[v][d % w] |= opened[v][nd];
      }
    }
    for (int(d) = 0; (d) < (D); ++(d)) {
      for (int v : ds[g]) {
        wei[g][d] += ms[v][d % w];
      }
    }
  }
  int s = belong[0];
  reach[s][0] = true;
  for (int(v) = 0; (v) < (grp); ++(v)) {
    for (int(d) = 0; (d) < (D); ++(d)) {
      MX(dp[v][d], wei[v][d]);
    }
    sort((des[v]).begin(), (des[v]).end());
    des[v].erase(unique((des[v]).begin(), (des[v]).end()), des[v].end());
    for (auto &p : des[v]) {
      int u = p.first;
      int off = p.second;
      for (int(d) = 0; (d) < (D); ++(d)) {
        if (reach[v][d]) {
          reach[u][(d + off) % D] = true;
          MX(dp[u][(d + off) % D], dp[v][d] + wei[u][(d + off) % D]);
        }
      }
    }
  }
  int ans = 0;
  for (int(v) = 0; (v) < (grp); ++(v)) {
    for (int(d) = 0; (d) < (D); ++(d)) {
      if (!reach[v][d]) continue;
      MX(ans, dp[v][d]);
    }
  }
  printf("%d\n", ans);
}
