#include <bits/stdc++.h>
using namespace std;
vector<int> adj[100005];
char s[100005][50];
int dorder[100005], sccOrd[100005];
int pos = 1, sccPos = 1;
stack<int> st;
int dfs(int node) {
  int ret = dorder[node] = pos++;
  st.push(node);
  for (int adjNode : adj[node]) {
    if (dorder[adjNode] == 0) {
      ret = min(ret, dfs(adjNode));
    } else if (sccOrd[adjNode] == 0) {
      ret = min(ret, dorder[adjNode]);
    }
  }
  if (ret == dorder[node]) {
    while (!st.empty()) {
      int cur = st.top();
      st.pop();
      sccOrd[cur] = sccPos;
      if (cur == node) {
        break;
      }
    }
    ++sccPos;
  }
  return ret;
}
int dsu[100005 * 50], val[100005 * 50];
int f(int x) {
  if (x == dsu[x]) {
    return x;
  }
  return dsu[x] = f(dsu[x]);
}
bool merge(int x, int y) {
  x = f(x), y = f(y);
  if (x != y) {
    dsu[x] = y;
    return true;
  }
  return false;
}
int dp[100005 * 50];
vector<pair<int, int> > dsuVer[100005 * 50];
int solveDp(int node, int day, int m) {
  int id = (node - 1) * m + day + 1;
  id = f(id);
  int &ret = dp[id];
  if (ret != -1) {
    return ret;
  }
  ret = 0;
  int sc = sccOrd[node], nday, tid;
  for (pair<int, int> tmp : dsuVer[id]) {
    nday = (tmp.second + 1) % m;
    for (int adjNode : adj[tmp.first]) {
      if (sccOrd[adjNode] != sccOrd[tmp.first]) {
        ret = max(ret, solveDp(adjNode, nday, m));
      }
    }
  }
  ret += val[id];
  return ret;
}
bool vis[100005 * 50];
bool canGo[100005 * 50];
void solve() {
  int n, k, d;
  scanf(" %d %d %d ", &n, &k, &d);
  for (int i = 0; i < k; ++i) {
    int u, v;
    scanf("%d %d", &u, &v);
    adj[u].push_back(v);
  }
  for (int i = 1; i <= n; ++i) {
    scanf(" %s ", s[i]);
  }
  dfs(1);
  for (int i = 1; i <= n; ++i) {
    for (int j = 0; j < d; ++j) {
      int id = (i - 1) * d + j + 1;
      dp[id] = -1;
      dsu[id] = id;
    }
  }
  queue<pair<int, int> > q;
  q.push({1, 0});
  int nday, id, tid;
  pair<int, int> tmp;
  while (!q.empty()) {
    tmp = q.front();
    q.pop();
    id = (tmp.first - 1) * d + tmp.second + 1;
    canGo[id] = true;
    nday = (tmp.second + 1) % d;
    for (int adjNode : adj[tmp.first]) {
      tid = (adjNode - 1) * d + nday + 1;
      if (!canGo[tid]) {
        canGo[tid] = true;
        q.push({adjNode, nday});
      }
    }
  }
  for (int i = 1; i <= n; ++i) {
    if (dorder[i] != 0) {
      for (int j = 0; j < d; ++j) {
        int id = (i - 1) * d + j + 1;
        if (!canGo[id]) {
          continue;
        }
        int nj = (j + 1) % d;
        for (int adjNode : adj[i]) {
          if (sccOrd[i] == sccOrd[adjNode]) {
            int tid = (adjNode - 1) * d + nj + 1;
            merge(id, tid);
          }
        }
      }
    }
  }
  for (int i = 1; i <= n; ++i) {
    if (dorder[i] != 0) {
      for (int j = 0; j < d; ++j) {
        int id = f((i - 1) * d + j + 1);
        if (!canGo[id]) {
          continue;
        }
        if (s[i][j] == '1') {
          if (!vis[id]) {
            ++val[id];
            vis[id] = true;
          }
        }
        dsuVer[f(id)].push_back({i, j});
      }
      for (int j = 0; j < d; ++j) {
        int id = f((i - 1) * d + j + 1);
        vis[id] = false;
      }
    }
  }
  printf("%d\n", solveDp(1, 0, d));
}
int main() {
  solve();
  return 0;
}
