#include <bits/stdc++.h>
 
using namespace std;
using ll = long long;
// #define int ll
using PII = pair<int, int>;
template <typename T> using V = vector<T>;
template <typename T> using VV = vector<V<T>>;
template <typename T> using VVV = vector<VV<T>>;
 
#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()
#define PB push_back
 
const ll INF = (1LL<<60);
const int MOD = 1000000007;
 
template <typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template <typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }
template <typename T> bool IN(T a, T b, T x) { return a<=x&&x<b; }
template<typename T> T ceil(T a, T b) { return a/b + !!(a%b); }
template<class S,class T>
ostream &operator <<(ostream& out,const pair<S,T>& a){
  out<<'('<<a.first<<','<<a.second<<')';
  return out;
}
template<class T>
ostream &operator <<(ostream& out,const vector<T>& a){
  out<<'[';
  REP(i, a.size()) {out<<a[i];if(i!=a.size()-1)out<<',';}
  out<<']';
  return out;
}
 
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};

// 文字の種類数をtemplate引数で渡す
template <int types = 26>
struct AhoCorasick {
  // trie木のnode
  struct node {
    int fail;
    V<int> next;
    V<int> matched;
    node() : fail(-1), next(types, -1) {}
  };

  // node の集合
  V<node> nodes;
  // 辞書の種類数, trie木の根
  int sz, root;
  // 文字と数字の対応付けをする関数
  function<int(char)> trans;

  // 初期化
  AhoCorasick() {}
  AhoCorasick(V<string> pattern, function<int(char)> f = [](char c){return c-'a';}) 
    : sz(pattern.size()), root(0)
  {
    nodes.resize(1);
    trans = f;
    build(pattern);
  }
  // vectorを結合
  V<int> unite(const V<int> &a, const V<int> &b) {
    V<int> ret;
    set_union(ALL(a), ALL(b), back_inserter(ret));
    return ret;
  }
  // 文字列集合patternからtrie木っぽいオートマトンを作成
  void build(V<string> pattern) {
    int now;
    nodes[root].fail = root;
    REP(i, pattern.size()) {
      now = root;
      for(const auto &c: pattern[i]) {
        if(nodes[now].next[trans(c)] == -1) {
          nodes.push_back(node());
          nodes[now].next[trans(c)] = nodes.size() - 1;
        }
        now = nodes[now].next[trans(c)];
      }
      nodes[now].matched.push_back(i);
    }

    queue<int> que;
    REP(i, types) {
      if(nodes[root].next[i] == -1) {
        nodes[root].next[i] = root;
      } else {
        nodes[nodes[root].next[i]].fail = root;
        que.push(nodes[root].next[i]);
      }
    }
    while(que.size()) {
      now = que.front(); que.pop();
      REP(i, types) {
        if(nodes[now].next[i] != -1) {
          int nxt = nodes[now].fail;
          while(nodes[nxt].next[i] == -1) nxt = nodes[nxt].fail;
          int nxt_tmp = nodes[now].next[i];
          nodes[nxt_tmp].fail = nodes[nxt].next[i];
          nodes[nxt_tmp].matched
            = unite(nodes[nxt_tmp].matched, nodes[nodes[nxt].next[i]].matched);
          que.push(nxt_tmp);
        }
      }
    }
  }
  // 一文字ずつ照合していく
  int next(int p, const char c) {
    while(nodes[p].next[trans(c)] == -1) p = nodes[p].fail;
    return nodes[p].next[trans(c)];
  }
  // 文字列s中に辞書と一致する部分列がどれだけあるか
  V<int> match(const string s) {
    V<int> res(sz);
    int now = root;
    for(auto c : s) {
      now = next(now, c);
      for(auto i : nodes[now].matched) res[i]++;
    }
    return res;
  }
};

map<int, int> dp[505][505][2];
signed main(){
  cin.tie(0);
  ios::sync_with_stdio(false);
 
  while(1) {
    int n, m, K;
    cin >> n >> m >> K;
    if(!n) break;
    V<string> from(n), to(n), sw(K), word(2*n);
    REP(i, n) {
      cin >> from[i] >> to[i];
      word[i] = from[i];
      word[n+i] = to[i];
    }
    REP(i, K) cin >> sw[i];

    REP(i, 505) REP(j, 505) REP(k, 2) dp[i][j][k].clear();

    // 単語iから単語jにつながるか？のリストをつくる
    sort(ALL(word));
    word.erase(unique(ALL(word)), word.end());
    VV<int> g(word.size());
    REP(i, n) {
      // from[i] から to[i] へつなぐ
      int s = -1, t = -1;
      REP(j, word.size()) {
        if(word[j] == from[i]) s = j;
        if(word[j] == to[i]) t = j;
      }
      g[s].PB(t);
    }

    AhoCorasick<26> aho(sw);
    // 初期状態
    REP(to, word.size()) {
      if(word[to].size() > m) continue;
      int now = 0;
      int num = 0;
      for(auto c: word[to]) {
        now = aho.next(now, c);
        num += aho.nodes[now].matched.size();
      }
      if(num >= 2) continue;
      (dp[word[to].size()][to][num][now] += 1) %= MOD;
    }

    // dp[文字列長i][最後の単語k][季語があるかl][あほこらの位置j]
    REP(i, m) {
      REP(j, aho.nodes.size()) REP(k, word.size()) REP(l, 2) {
        if(dp[i][k][l].find(j) == dp[i][k][l].end()) continue;
        for(auto to: g[k]) {
          // toに対しての遷移 dp[i+word[to].size()][to][num][now]
          if(i+word[to].size() > m) continue;
          int now = j;
          int num = l;
          bool flag = false;
          for(auto c: word[to]) {
            now = aho.next(now, c);
            num += aho.nodes[now].matched.size();
          }
          if(num >= 2) continue;
          (dp[i+word[to].size()][to][num][now] += dp[i][k][l][j]) %= MOD;
        }
      }
      REP(k, word.size()) REP(l, 2) dp[i][k][l].clear();
    }

    int ret = 0;
    REP(i, aho.nodes.size()) REP(j, word.size()) {
      (ret += dp[m][j][1][i]) %= MOD;
      // cout << dp[m][j][1][i] << endl;
    }
    cout << ret << endl;
  }
  
  return 0;
}


Measured memory_bytes: 40892