/**
 * code generated by JHelper
 * More info: https://github.com/AlexeyDmitriev/JHelper
 * @author zjsdut
 */

#include <bits/stdc++.h>

using namespace std;

struct fast_ios {
  fast_ios() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
  };
} fast_ios_;

template <typename A, typename B> bool chkmin(A &a, const B &b) {
  if (b < a) {
    a = b;
    return true;
  }
  return false;
}

template <typename A, typename B> bool chkmax(A &a, const B &b) {
  if (b > a) {
    a = b;
    return true;
  }
  return false;
}

template <typename T> istream &operator>>(istream &stream, vector<T> &vec) {
  for (auto &x : vec)
    stream >> x;
  return stream;
}

void scan() {}

template <class T, class... Args> void scan(T &a, Args &... rest) {
  cin >> a;
  scan(rest...);
}

using ll = long long;
using vl = vector<ll>;
using vb = vector<bool>;
using vi = vector<int>;
using pii = pair<int, int>;
template <typename T> using vv = vector<vector<T>>;
#define rng(i, a, b) for (int i = (int)(a); i < (int)(b); ++i)
#define up(i, a, b) for (int i = int(a); i <= int(b); ++i)
#define down(i, b, a) for (int i = int(b); i >= int(a); i--)
#define TOKENPASTE(x, y) x##y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define rep(n)                                                                 \
  for (int TOKENPASTE2(_iter_, __LINE__) = 0,                                  \
                               TOKENPASTE2(_num_, __LINE__) = (int)(n);        \
       TOKENPASTE2(_iter_, __LINE__) < TOKENPASTE2(_num_, __LINE__);           \
       ++TOKENPASTE2(_iter_, __LINE__))
#define FOR(x, cont) for (const auto &x : cont)
#define For(x, cont) for (auto &x : cont)
#define all(x) begin(x), end(x)
#define pb push_back
#define mp make_pair
#define eb emplace_back
#define ep emplace
#define SZ(x) (int)(x).size()
#define UNIQ(vec) (vec).erase(unique(all(vec)), end(vec))
#define LB(cont, x) int(lower_bound(all(cont), x) - begin(cont))
#define UB(cont, x) int(upper_bound(all(cont), x) - begin(cont))

template <typename T>
ostream &operator<<(ostream &stream, const vector<T> &vec) {
  if (!vec.empty()) {
    stream << vec[0];
    for (size_t i = 1; i != vec.size(); ++i)
      stream << ' ' << vec[i];
  }
  return stream;
}

template <typename T> void print(const vector<T> &t) { cout << t << '\n'; }

template <typename T> void print(const vector<vector<T>> &t) {
  for (const auto &row : t) {
    print(row);
  }
}

template <typename T> void print(const T &t) { cout << t << ' '; }

template <typename T, typename... Args>
void print(const T &t, const Args &... rest) {
  print(t);
  print(rest...);
}

template <typename T> void println(const T &t) { cout << t << '\n'; }

template <typename T, typename... Args>
void println(const T &t, const Args &... rest) {
  print(t);
  println(rest...);
}

// tourist's modular-arithmetic class
template <typename T> T inverse(T a, T m) {
  T u = 0, v = 1;
  while (a != 0) {
    T t = m / a;
    m -= t * a;
    swap(a, m);
    u -= t * v;
    swap(u, v);
  }
  assert(m == 1);
  return u;
}

template <typename T> class Modular {
public:
  using Type = typename decay<decltype(T::value)>::type;

  constexpr Modular() : value() {}

  template <typename U> Modular(const U &x) { value = normalize(x); }

  template <typename U> static Type normalize(const U &x) {
    Type v;
    if (-mod() <= x && x < mod())
      v = static_cast<Type>(x);
    else
      v = static_cast<Type>(x % mod());
    if (v < 0)
      v += mod();
    return v;
  }

  const Type &operator()() const { return value; }

  template <typename U> explicit operator U() const {
    return static_cast<U>(value);
  }

  constexpr static Type mod() { return T::value; }

  Modular &operator+=(const Modular &other) {
    if ((value += other.value) >= mod())
      value -= mod();
    return *this;
  }

  Modular &operator-=(const Modular &other) {
    if ((value -= other.value) < 0)
      value += mod();
    return *this;
  }

  template <typename U> Modular &operator+=(const U &other) {
    return *this += Modular(other);
  }

  template <typename U> Modular &operator-=(const U &other) {
    return *this -= Modular(other);
  }

  Modular &operator++() { return *this += 1; }

  Modular &operator--() { return *this -= 1; }

  Modular operator++(int) {
    Modular result(*this);
    *this += 1;
    return result;
  }

  Modular operator--(int) {
    Modular result(*this);
    *this -= 1;
    return result;
  }

  Modular operator-() const { return Modular(-value); }

  template <typename U = T>
  enable_if_t<is_same<typename Modular<U>::Type, int>::value, Modular> &
  operator*=(const Modular &rhs) {
#ifdef _WIN32
    uint64_t x = static_cast<int64_t>(value) * static_cast<int64_t>(rhs.value);
    uint32_t xh = static_cast<uint32_t>(x >> 32), xl = static_cast<uint32_t>(x),
             d, m;
    asm("divl %4; \n\t" : "=a"(d), "=d"(m) : "d"(xh), "a"(xl), "r"(mod()));
    value = m;
#else
    value = normalize(static_cast<int64_t>(value) *
                      static_cast<int64_t>(rhs.value));
#endif
    return *this;
  }

  template <typename U = T>
  enable_if_t<is_same<typename Modular<U>::Type, int64_t>::value, Modular> &
  operator*=(const Modular &rhs) {
    int64_t q = static_cast<int64_t>(static_cast<long double>(value) *
                                     rhs.value / mod());
    value = normalize(value * rhs.value - q * mod());
    return *this;
  }

  template <typename U = T>
  enable_if_t<!is_integral<typename Modular<U>::Type>::value, Modular> &
  operator*=(const Modular &rhs) {
    value = normalize(value * rhs.value);
    return *this;
  }

  Modular &operator/=(const Modular &other) {
    return *this *= Modular(inverse(other.value, mod()));
  }

  template <typename U> friend const Modular<U> &abs(const Modular<U> &v) {
    return v;
  }

  template <typename U>
  friend bool operator==(const Modular<U> &lhs, const Modular<U> &rhs);

  template <typename U>
  friend bool operator<(const Modular<U> &lhs, const Modular<U> &rhs);

  template <typename U>
  friend std::istream &operator>>(std::istream &stream, Modular<U> &number);

private:
  Type value;
};

template <typename T>
bool operator==(const Modular<T> &lhs, const Modular<T> &rhs) {
  return lhs.value == rhs.value;
}

template <typename T, typename U>
bool operator==(const Modular<T> &lhs, U rhs) {
  return lhs == Modular<T>(rhs);
}

template <typename T, typename U>
bool operator==(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) == rhs;
}

template <typename T>
bool operator!=(const Modular<T> &lhs, const Modular<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T, typename U>
bool operator!=(const Modular<T> &lhs, U rhs) {
  return !(lhs == rhs);
}

template <typename T, typename U>
bool operator!=(U lhs, const Modular<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T>
bool operator<(const Modular<T> &lhs, const Modular<T> &rhs) {
  return lhs.value < rhs.value;
}

template <typename T>
Modular<T> operator+(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T, typename U>
Modular<T> operator+(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T, typename U>
Modular<T> operator+(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T>
Modular<T> operator-(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T, typename U>
Modular<T> operator-(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T, typename U>
Modular<T> operator-(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T>
Modular<T> operator*(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T, typename U>
Modular<T> operator*(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T, typename U>
Modular<T> operator*(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T>
Modular<T> operator/(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> operator/(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> operator/(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> power(const Modular<T> &a, const U &b) {
  assert(b >= 0);
  Modular<T> x = a, res = 1;
  U p = b;
  while (p > 0) {
    if (p & 1)
      res *= x;
    x *= x;
    p >>= 1;
  }
  return res;
}

template <typename T> bool IsZero(const Modular<T> &number) {
  return number() == 0;
}

template <typename T> string to_string(const Modular<T> &number) {
  return to_string(number());
}

template <typename T>
std::ostream &operator<<(std::ostream &stream, const Modular<T> &number) {
  return stream << number();
}

template <typename T>
std::istream &operator>>(std::istream &stream, Modular<T> &number) {
  typename common_type<typename Modular<T>::Type, int64_t>::type x;
  stream >> x;
  number.value = Modular<T>::normalize(x);
  return stream;
}

// debug code by tourist
string to_string(const string &s) { return '"' + s + '"'; }

string to_string(const char *s) { return to_string((string)s); }

string to_string(bool b) { return (b ? "true" : "false"); }

template <typename A, typename B> string to_string(pair<A, B> p) {
  return "(" + to_string(p.first) + ", " + to_string(p.second) + ")";
}

template <typename A> string to_string(A v) {
  bool first = true;
  string res = "{";
  for (const auto &x : v) {
    if (!first) {
      res += ", ";
    }
    first = false;
    res += to_string(x);
  }
  res += "}";
  return res;
}

void debug_out() { cerr << endl; }

template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) {
  cerr << " " << to_string(H);
  debug_out(T...);
}

#if defined LOCAL && !defined DUIPAI
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif

class UnionFind {
  vi par, sz;
  int nTree;

public:
  explicit UnionFind(int n) : par(n), sz(n, 1), nTree(n) { // 0-indexed
    iota(par.begin(), par.end(), 0);
  }

  int n_tree() const { return nTree; }

  int size(int x) { return sz[root(x)]; }

  int root(int x) { return x == par[x] ? x : par[x] = root(par[x]); }

  bool unite(int x, int y) {
    int rx = root(x), ry = root(y);
    if (rx != ry) {
      par[rx] = ry;
      --nTree;
      sz[ry] += sz[rx];
      return true;
    }
    return false;
  }

  bool is_root(int x) const { return par[x] == x; }
  bool same(int x, int y) { return root(x) == root(y); }
};

template <typename T> class BIT {
  using BinOp = function<T(T, T)>;
  BinOp bin_op;
  const T init;
  // 0-indexed
  vector<T> a;

public:
  explicit BIT(
      int n, BinOp op = [](T x, T y) { return x + y; }, T init = T{})
      : bin_op(std::move(op)), init(init), a(n, init) {}

  T prefix(int x) const {
    auto res = init;
    while (x >= 0) {
      res = bin_op(a[x], res);
      x = (x & (x + 1)) - 1;
    }
    return res;
  }

  void modify(int x, T v) {
    while (x < SZ(a)) {
      a[x] = bin_op(a[x], v);
      x |= (x + 1);
    }
  }

  void clear() { fill(a.begin(), a.end(), init); }
};

template <typename int_t> inline int_t lowbit(int_t x) { return x & -x; }

inline unsigned h_bit(unsigned long long x) {
  return sizeof(unsigned long long) * 8 - __builtin_clzll(x);
}

unsigned long long pow2(unsigned long long x) { // round up to power of 2
  return x == lowbit(x) ? x : 1ull << h_bit(x);
}

template <typename T> int get_bit(T a, int i) { return a >> i & 1; }
template <typename T> T get_mid(T l, T r) {
  assert(l <= r);
  return l + (r - l) / 2;
}

#define popcnt(x) __builtin_popcountll((x))
constexpr int md = 1000000007;
using Mint = Modular<std::integral_constant<decay<decltype(md)>::type, md>>;
// ftiasch's AC automaton template
template <int N, int C> struct AhoCorasick {
  AhoCorasick() : n(1) {
    memset(weight, 0, sizeof(weight));
    memset(go, -1, sizeof(go));
  }

  void insert(const char *s, int w = 1) {
    int p = 0;
    for (int i = 0; s[i]; ++i) {
      int c = s[i] - 'a';
      if (go[p][c] == -1) {
        go[p][c] = n++;
      }
      p = go[p][c];
    }
    weight[p] += w;
  }

  void initialize() {
    std::queue<int> queue;
    for (int c = 0; c < C; ++c) {
      int &v = go[0][c];
      if (~v) {
        queue.push(v);
      }
      (~v ? fail[v] : v) = 0;
    }
    while (!queue.empty()) {
      int u = queue.front();
      queue.pop();
      weight[u] += weight[fail[u]];
      for (int c = 0; c < C; ++c) {
        int &v = go[u][c];
        if (~v) {
          queue.push(v);
        }
        (~v ? fail[v] : v) = go[fail[u]][c];
      }
    }
  }

  int n, weight[N]{}, go[N][C]{}, fail[N]{};
};

class GLettersAndQuestionMarks {
  static void SOLVE() {
    const int N = 1001;
    const int M = 14;
    AhoCorasick<N, M> ac;
    int k;
    scan(k);
    int max_length = 0;
    rep(k) {
      string s;
      int c;
      scan(s, c);
      chkmax(max_length, SZ(s));
      ac.insert(s.c_str(), c);
    }
    ac.initialize();
    int n = ac.n;
    // cache friendly?
    vv<int> tgo(M, vi(n));
    rng (i, 0, n) {
      rng (j, 0, M) {
        tgo[j][i] = ac.go[i][j];
      }
    }
    debug(n);
    // dp[i][mask] : max value obtained after the first some '?'s determined as
    // specified in mask and arrived at state i of the AC automaton.
    vv<ll> dp(n, vl(1 << M, LLONG_MIN));
    // gain[i]: value gained by traversing the current substring from state i
    vl gain(n);
    // dest[i]: new state reached after traversing the current substring from
    // state i
    vi dest(n);
    iota(all(dest), 0);
    // number of '?' processed so far.
    int m = 0;
    string s;
    scan(s);
    dp[0][0] = 0;
    int sole_dest;
    ll sole_gain;
    int len = 0;
    auto get_dest = [&](int i) {
      return len >= max_length ? sole_dest : dest[i];
    };
    auto get_gain = [&](int i) {
      return len >= max_length ? sole_gain + gain[i] : gain[i];
    };
    FOR(x, s) {
      if (x == '?') {
        rng(mask, 0, 1 << M) {
          if (popcnt(mask) == m) {
            rng(i, 0, n) {
              if (dp[i][mask] != LLONG_MIN) {
                // choose a letter
                rng(c, 0, M) {
                  if (!get_bit(mask, c)) {
                    int j = ac.go[get_dest(i)][c];
                    chkmax(dp[j][mask | 1 << c],
                           dp[i][mask] + get_gain(i) + ac.weight[j]);
                  }
                }
              }
            }
          }
        }
        ++m;
        fill(all(gain), 0);
        iota(all(dest), 0);
        len = 0;
      } else {
        int c = x - 'a';
        ++len;
        if (len < max_length) {
          rng(i, 0, n) {
//            dest[i] = ac.go[dest[i]][c];
            dest[i] = tgo[c][dest[i]];
            gain[i] += ac.weight[dest[i]];
          }
        } else {
          if (len == max_length) {
            sole_dest = dest[0];
            sole_gain = 0;
          }
//          sole_dest = ac.go[sole_dest][c];
          sole_dest = tgo[c][sole_dest];
          sole_gain += ac.weight[sole_dest];
        }
      }
    }
    ll ans = LLONG_MIN;
    rng(mask, 0, 1 << M) {
      if (popcnt(mask) == m) {
        rng(i, 0, n) {
          if (dp[i][mask] != LLONG_MIN) {
            chkmax(ans, dp[i][mask] + get_gain(i));
          }
        }
      }
    }
    println(ans);
//    debug(ac.n);
//    auto output = [&]() {
//      cout << "digraph{\n";
//      rng (i, 0, ac.n) {
//        rng (j, 0, 3) {
//          cout << i << "->" << ac.go[i][j] << " [label=" << char('a' + j)  << "]\n";
//        }
//      }
//      cout << "}";
//    };
//    output();
  }

public:
  static void solve(std::istream &in, std::ostream &out) {
    auto cin_buff = std::cin.rdbuf(in.rdbuf());
    auto cerr_buff = std::cerr.rdbuf(cout.rdbuf(out.rdbuf()));
    SOLVE();
    std::cout.rdbuf(cerr.rdbuf(cerr_buff));
    std::cin.rdbuf(cin_buff);
  }
};

int main() {
  GLettersAndQuestionMarks solver;
  std::istream& in(std::cin);
  std::ostream& out(std::cout);
  solver.solve(in, out);
  return 0;
}


/**
 * code generated by JHelper
 * More info: https://github.com/AlexeyDmitriev/JHelper
 * @author zjsdut
 */

#include <bits/stdc++.h>

using namespace std;

struct fast_ios {
  fast_ios() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
  };
} fast_ios_;

template <typename A, typename B> bool chkmin(A &a, const B &b) {
  if (b < a) {
    a = b;
    return true;
  }
  return false;
}

template <typename A, typename B> bool chkmax(A &a, const B &b) {
  if (b > a) {
    a = b;
    return true;
  }
  return false;
}

template <typename T> istream &operator>>(istream &stream, vector<T> &vec) {
  for (auto &x : vec)
    stream >> x;
  return stream;
}

void scan() {}

template <class T, class... Args> void scan(T &a, Args &... rest) {
  cin >> a;
  scan(rest...);
}

using ll = long long;
using vl = vector<ll>;
using vb = vector<bool>;
using vi = vector<int>;
using pii = pair<int, int>;
template <typename T> using vv = vector<vector<T>>;
#define rng(i, a, b) for (int i = (int)(a); i < (int)(b); ++i)
#define up(i, a, b) for (int i = int(a); i <= int(b); ++i)
#define down(i, b, a) for (int i = int(b); i >= int(a); i--)
#define TOKENPASTE(x, y) x##y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define rep(n)                                                                 \
  for (int TOKENPASTE2(_iter_, __LINE__) = 0,                                  \
                               TOKENPASTE2(_num_, __LINE__) = (int)(n);        \
       TOKENPASTE2(_iter_, __LINE__) < TOKENPASTE2(_num_, __LINE__);           \
       ++TOKENPASTE2(_iter_, __LINE__))
#define FOR(x, cont) for (const auto &x : cont)
#define For(x, cont) for (auto &x : cont)
#define all(x) begin(x), end(x)
#define pb push_back
#define mp make_pair
#define eb emplace_back
#define ep emplace
#define SZ(x) (int)(x).size()
#define UNIQ(vec) (vec).erase(unique(all(vec)), end(vec))
#define LB(cont, x) int(lower_bound(all(cont), x) - begin(cont))
#define UB(cont, x) int(upper_bound(all(cont), x) - begin(cont))

template <typename T>
ostream &operator<<(ostream &stream, const vector<T> &vec) {
  if (!vec.empty()) {
    stream << vec[0];
    for (size_t i = 1; i != vec.size(); ++i)
      stream << ' ' << vec[i];
  }
  return stream;
}

template <typename T> void print(const vector<T> &t) { cout << t << '\n'; }

template <typename T> void print(const vector<vector<T>> &t) {
  for (const auto &row : t) {
    print(row);
  }
}

template <typename T> void print(const T &t) { cout << t << ' '; }

template <typename T, typename... Args>
void print(const T &t, const Args &... rest) {
  print(t);
  print(rest...);
}

template <typename T> void println(const T &t) { cout << t << '\n'; }

template <typename T, typename... Args>
void println(const T &t, const Args &... rest) {
  print(t);
  println(rest...);
}

// tourist's modular-arithmetic class
template <typename T> T inverse(T a, T m) {
  T u = 0, v = 1;
  while (a != 0) {
    T t = m / a;
    m -= t * a;
    swap(a, m);
    u -= t * v;
    swap(u, v);
  }
  assert(m == 1);
  return u;
}

template <typename T> class Modular {
public:
  using Type = typename decay<decltype(T::value)>::type;

  constexpr Modular() : value() {}

  template <typename U> Modular(const U &x) { value = normalize(x); }

  template <typename U> static Type normalize(const U &x) {
    Type v;
    if (-mod() <= x && x < mod())
      v = static_cast<Type>(x);
    else
      v = static_cast<Type>(x % mod());
    if (v < 0)
      v += mod();
    return v;
  }

  const Type &operator()() const { return value; }

  template <typename U> explicit operator U() const {
    return static_cast<U>(value);
  }

  constexpr static Type mod() { return T::value; }

  Modular &operator+=(const Modular &other) {
    if ((value += other.value) >= mod())
      value -= mod();
    return *this;
  }

  Modular &operator-=(const Modular &other) {
    if ((value -= other.value) < 0)
      value += mod();
    return *this;
  }

  template <typename U> Modular &operator+=(const U &other) {
    return *this += Modular(other);
  }

  template <typename U> Modular &operator-=(const U &other) {
    return *this -= Modular(other);
  }

  Modular &operator++() { return *this += 1; }

  Modular &operator--() { return *this -= 1; }

  Modular operator++(int) {
    Modular result(*this);
    *this += 1;
    return result;
  }

  Modular operator--(int) {
    Modular result(*this);
    *this -= 1;
    return result;
  }

  Modular operator-() const { return Modular(-value); }

  template <typename U = T>
  enable_if_t<is_same<typename Modular<U>::Type, int>::value, Modular> &
  operator*=(const Modular &rhs) {
#ifdef _WIN32
    uint64_t x = static_cast<int64_t>(value) * static_cast<int64_t>(rhs.value);
    uint32_t xh = static_cast<uint32_t>(x >> 32), xl = static_cast<uint32_t>(x),
             d, m;
    asm("divl %4; \n\t" : "=a"(d), "=d"(m) : "d"(xh), "a"(xl), "r"(mod()));
    value = m;
#else
    value = normalize(static_cast<int64_t>(value) *
                      static_cast<int64_t>(rhs.value));
#endif
    return *this;
  }

  template <typename U = T>
  enable_if_t<is_same<typename Modular<U>::Type, int64_t>::value, Modular> &
  operator*=(const Modular &rhs) {
    int64_t q = static_cast<int64_t>(static_cast<long double>(value) *
                                     rhs.value / mod());
    value = normalize(value * rhs.value - q * mod());
    return *this;
  }

  template <typename U = T>
  enable_if_t<!is_integral<typename Modular<U>::Type>::value, Modular> &
  operator*=(const Modular &rhs) {
    value = normalize(value * rhs.value);
    return *this;
  }

  Modular &operator/=(const Modular &other) {
    return *this *= Modular(inverse(other.value, mod()));
  }

  template <typename U> friend const Modular<U> &abs(const Modular<U> &v) {
    return v;
  }

  template <typename U>
  friend bool operator==(const Modular<U> &lhs, const Modular<U> &rhs);

  template <typename U>
  friend bool operator<(const Modular<U> &lhs, const Modular<U> &rhs);

  template <typename U>
  friend std::istream &operator>>(std::istream &stream, Modular<U> &number);

private:
  Type value;
};

template <typename T>
bool operator==(const Modular<T> &lhs, const Modular<T> &rhs) {
  return lhs.value == rhs.value;
}

template <typename T, typename U>
bool operator==(const Modular<T> &lhs, U rhs) {
  return lhs == Modular<T>(rhs);
}

template <typename T, typename U>
bool operator==(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) == rhs;
}

template <typename T>
bool operator!=(const Modular<T> &lhs, const Modular<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T, typename U>
bool operator!=(const Modular<T> &lhs, U rhs) {
  return !(lhs == rhs);
}

template <typename T, typename U>
bool operator!=(U lhs, const Modular<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T>
bool operator<(const Modular<T> &lhs, const Modular<T> &rhs) {
  return lhs.value < rhs.value;
}

template <typename T>
Modular<T> operator+(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T, typename U>
Modular<T> operator+(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T, typename U>
Modular<T> operator+(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T>
Modular<T> operator-(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T, typename U>
Modular<T> operator-(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T, typename U>
Modular<T> operator-(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T>
Modular<T> operator*(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T, typename U>
Modular<T> operator*(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T, typename U>
Modular<T> operator*(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T>
Modular<T> operator/(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> operator/(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> operator/(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> power(const Modular<T> &a, const U &b) {
  assert(b >= 0);
  Modular<T> x = a, res = 1;
  U p = b;
  while (p > 0) {
    if (p & 1)
      res *= x;
    x *= x;
    p >>= 1;
  }
  return res;
}

template <typename T> bool IsZero(const Modular<T> &number) {
  return number() == 0;
}

template <typename T> string to_string(const Modular<T> &number) {
  return to_string(number());
}

template <typename T>
std::ostream &operator<<(std::ostream &stream, const Modular<T> &number) {
  return stream << number();
}

template <typename T>
std::istream &operator>>(std::istream &stream, Modular<T> &number) {
  typename common_type<typename Modular<T>::Type, int64_t>::type x;
  stream >> x;
  number.value = Modular<T>::normalize(x);
  return stream;
}

// debug code by tourist
string to_string(const string &s) { return '"' + s + '"'; }

string to_string(const char *s) { return to_string((string)s); }

string to_string(bool b) { return (b ? "true" : "false"); }

template <typename A, typename B> string to_string(pair<A, B> p) {
  return "(" + to_string(p.first) + ", " + to_string(p.second) + ")";
}

template <typename A> string to_string(A v) {
  bool first = true;
  string res = "{";
  for (const auto &x : v) {
    if (!first) {
      res += ", ";
    }
    first = false;
    res += to_string(x);
  }
  res += "}";
  return res;
}

void debug_out() { cerr << endl; }

template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) {
  cerr << " " << to_string(H);
  debug_out(T...);
}

#if defined LOCAL && !defined DUIPAI
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif

class UnionFind {
  vi par, sz;
  int nTree;

public:
  explicit UnionFind(int n) : par(n), sz(n, 1), nTree(n) { // 0-indexed
    iota(par.begin(), par.end(), 0);
  }

  int n_tree() const { return nTree; }

  int size(int x) { return sz[root(x)]; }

  int root(int x) { return x == par[x] ? x : par[x] = root(par[x]); }

  bool unite(int x, int y) {
    int rx = root(x), ry = root(y);
    if (rx != ry) {
      par[rx] = ry;
      --nTree;
      sz[ry] += sz[rx];
      return true;
    }
    return false;
  }

  bool is_root(int x) const { return par[x] == x; }
  bool same(int x, int y) { return root(x) == root(y); }
};

template <typename T> class BIT {
  using BinOp = function<T(T, T)>;
  BinOp bin_op;
  const T init;
  // 0-indexed
  vector<T> a;

public:
  explicit BIT(
      int n, BinOp op = [](T x, T y) { return x + y; }, T init = T{})
      : bin_op(std::move(op)), init(init), a(n, init) {}

  T prefix(int x) const {
    auto res = init;
    while (x >= 0) {
      res = bin_op(a[x], res);
      x = (x & (x + 1)) - 1;
    }
    return res;
  }

  void modify(int x, T v) {
    while (x < SZ(a)) {
      a[x] = bin_op(a[x], v);
      x |= (x + 1);
    }
  }

  void clear() { fill(a.begin(), a.end(), init); }
};

template <typename int_t> inline int_t lowbit(int_t x) { return x & -x; }

inline unsigned h_bit(unsigned long long x) {
  return sizeof(unsigned long long) * 8 - __builtin_clzll(x);
}

unsigned long long pow2(unsigned long long x) { // round up to power of 2
  return x == lowbit(x) ? x : 1ull << h_bit(x);
}

template <typename T> int get_bit(T a, int i) { return a >> i & 1; }
template <typename T> T get_mid(T l, T r) {
  assert(l <= r);
  return l + (r - l) / 2;
}

#define popcnt(x) __builtin_popcountll((x))
constexpr int md = 1000000007;
using Mint = Modular<std::integral_constant<decay<decltype(md)>::type, md>>;
// ftiasch's AC automaton template
template <int N, int C> struct AhoCorasick {
  AhoCorasick() : n(1) {
    memset(weight, 0, sizeof(weight));
    memset(go, -1, sizeof(go));
  }

  void insert(const char *s, int w = 1) {
    int p = 0;
    for (int i = 0; s[i]; ++i) {
      int c = s[i] - 'a';
      if (go[p][c] == -1) {
        go[p][c] = n++;
      }
      p = go[p][c];
    }
    weight[p] += w;
  }

  void initialize() {
    std::queue<int> queue;
    for (int c = 0; c < C; ++c) {
      int &v = go[0][c];
      if (~v) {
        queue.push(v);
      }
      (~v ? fail[v] : v) = 0;
    }
    while (!queue.empty()) {
      int u = queue.front();
      queue.pop();
      weight[u] += weight[fail[u]];
      for (int c = 0; c < C; ++c) {
        int &v = go[u][c];
        if (~v) {
          queue.push(v);
        }
        (~v ? fail[v] : v) = go[fail[u]][c];
      }
    }
  }

  int n, weight[N]{}, go[N][C]{}, fail[N]{};
};

class GLettersAndQuestionMarks {
  static void SOLVE() {
    const int N = 1001;
    const int M = 14;
    AhoCorasick<N, M> ac;
    int k;
    scan(k);
    int max_length = 0;
    rep(k) {
      string s;
      int c;
      scan(s, c);
      chkmax(max_length, SZ(s));
      ac.insert(s.c_str(), c);
    }
    ac.initialize();
    int n = ac.n;
    // cache friendly?
    vv<int> tgo(M, vi(n));
    rng (i, 0, M) {
      rng (j, 0, n) {
        tgo[i][j] = ac.go[j][i];
      }
    }
    debug(n);
    // dp[i][mask] : max value obtained after the first some '?'s determined as
    // specified in mask and arrived at state i of the AC automaton.
    vv<ll> dp(n, vl(1 << M, LLONG_MIN));
    // gain[i]: value gained by traversing the current substring from state i
    vl gain(n);
    // dest[i]: new state reached after traversing the current substring from
    // state i
    vi dest(n);
    iota(all(dest), 0);
    // number of '?' processed so far.
    int m = 0;
    string s;
    scan(s);
    dp[0][0] = 0;
    int sole_dest;
    ll sole_gain;
    int len = 0;
    auto get_dest = [&](int i) {
      return len >= max_length ? sole_dest : dest[i];
    };
    auto get_gain = [&](int i) {
      return len >= max_length ? sole_gain + gain[i] : gain[i];
    };
    FOR(x, s) {
      if (x == '?') {
        rng(mask, 0, 1 << M) {
          if (popcnt(mask) == m) {
            rng(i, 0, n) {
              if (dp[i][mask] != LLONG_MIN) {
                // choose a letter
                rng(c, 0, M) {
                  if (!get_bit(mask, c)) {
                    int j = ac.go[get_dest(i)][c];
                    chkmax(dp[j][mask | 1 << c],
                           dp[i][mask] + get_gain(i) + ac.weight[j]);
                  }
                }
              }
            }
          }
        }
        ++m;
        fill(all(gain), 0);
        iota(all(dest), 0);
        len = 0;
      } else {
        int c = x - 'a';
        ++len;
        if (len < max_length) {
          rng(i, 0, n) {
//            dest[i] = ac.go[dest[i]][c];
            dest[i] = tgo[c][dest[i]];
            gain[i] += ac.weight[dest[i]];
          }
        } else {
          if (len == max_length) {
            sole_dest = dest[0];
            sole_gain = 0;
          }
//          sole_dest = ac.go[sole_dest][c];
          sole_dest = tgo[c][sole_dest];
          sole_gain += ac.weight[sole_dest];
        }
      }
    }
    ll ans = LLONG_MIN;
    rng(mask, 0, 1 << M) {
      if (popcnt(mask) == m) {
        rng(i, 0, n) {
          if (dp[i][mask] != LLONG_MIN) {
            chkmax(ans, dp[i][mask] + get_gain(i));
          }
        }
      }
    }
    println(ans);
//    debug(ac.n);
//    auto output = [&]() {
//      cout << "digraph{\n";
//      rng (i, 0, ac.n) {
//        rng (j, 0, 3) {
//          cout << i << "->" << ac.go[i][j] << " [label=" << char('a' + j)  << "]\n";
//        }
//      }
//      cout << "}";
//    };
//    output();
  }

public:
  static void solve(std::istream &in, std::ostream &out) {
    auto cin_buff = std::cin.rdbuf(in.rdbuf());
    auto cerr_buff = std::cerr.rdbuf(cout.rdbuf(out.rdbuf()));
    SOLVE();
    std::cout.rdbuf(cerr.rdbuf(cerr_buff));
    std::cin.rdbuf(cin_buff);
  }
};

int main() {
  GLettersAndQuestionMarks solver;
  std::istream& in(std::cin);
  std::ostream& out(std::cout);
  solver.solve(in, out);
  return 0;
}


/**
 * code generated by JHelper
 * More info: https://github.com/AlexeyDmitriev/JHelper
 * @author zjsdut
 */

#include <bits/stdc++.h>

using namespace std;

struct fast_ios {
  fast_ios() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
  };
} fast_ios_;

template <typename A, typename B> bool chkmin(A &a, const B &b) {
  if (b < a) {
    a = b;
    return true;
  }
  return false;
}

template <typename A, typename B> bool chkmax(A &a, const B &b) {
  if (b > a) {
    a = b;
    return true;
  }
  return false;
}

template <typename T> istream &operator>>(istream &stream, vector<T> &vec) {
  for (auto &x : vec)
    stream >> x;
  return stream;
}

void scan() {}

template <class T, class... Args> void scan(T &a, Args &... rest) {
  cin >> a;
  scan(rest...);
}

using ll = long long;
using vl = vector<ll>;
using vb = vector<bool>;
using vi = vector<int>;
using pii = pair<int, int>;
template <typename T> using vv = vector<vector<T>>;
#define rng(i, a, b) for (int i = (int)(a); i < (int)(b); ++i)
#define up(i, a, b) for (int i = int(a); i <= int(b); ++i)
#define down(i, b, a) for (int i = int(b); i >= int(a); i--)
#define TOKENPASTE(x, y) x##y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define rep(n)                                                                 \
  for (int TOKENPASTE2(_iter_, __LINE__) = 0,                                  \
                               TOKENPASTE2(_num_, __LINE__) = (int)(n);        \
       TOKENPASTE2(_iter_, __LINE__) < TOKENPASTE2(_num_, __LINE__);           \
       ++TOKENPASTE2(_iter_, __LINE__))
#define FOR(x, cont) for (const auto &x : cont)
#define For(x, cont) for (auto &x : cont)
#define all(x) begin(x), end(x)
#define pb push_back
#define mp make_pair
#define eb emplace_back
#define ep emplace
#define SZ(x) (int)(x).size()
#define UNIQ(vec) (vec).erase(unique(all(vec)), end(vec))
#define LB(cont, x) int(lower_bound(all(cont), x) - begin(cont))
#define UB(cont, x) int(upper_bound(all(cont), x) - begin(cont))

template <typename T>
ostream &operator<<(ostream &stream, const vector<T> &vec) {
  if (!vec.empty()) {
    stream << vec[0];
    for (size_t i = 1; i != vec.size(); ++i)
      stream << ' ' << vec[i];
  }
  return stream;
}

template <typename T> void print(const vector<T> &t) { cout << t << '\n'; }

template <typename T> void print(const vector<vector<T>> &t) {
  for (const auto &row : t) {
    print(row);
  }
}

template <typename T> void print(const T &t) { cout << t << ' '; }

template <typename T, typename... Args>
void print(const T &t, const Args &... rest) {
  print(t);
  print(rest...);
}

template <typename T> void println(const T &t) { cout << t << '\n'; }

template <typename T, typename... Args>
void println(const T &t, const Args &... rest) {
  print(t);
  println(rest...);
}

// tourist's modular-arithmetic class
template <typename T> T inverse(T a, T m) {
  T u = 0, v = 1;
  while (a != 0) {
    T t = m / a;
    m -= t * a;
    swap(a, m);
    u -= t * v;
    swap(u, v);
  }
  assert(m == 1);
  return u;
}

template <typename T> class Modular {
public:
  using Type = typename decay<decltype(T::value)>::type;

  constexpr Modular() : value() {}

  template <typename U> Modular(const U &x) { value = normalize(x); }

  template <typename U> static Type normalize(const U &x) {
    Type v;
    if (-mod() <= x && x < mod())
      v = static_cast<Type>(x);
    else
      v = static_cast<Type>(x % mod());
    if (v < 0)
      v += mod();
    return v;
  }

  const Type &operator()() const { return value; }

  template <typename U> explicit operator U() const {
    return static_cast<U>(value);
  }

  constexpr static Type mod() { return T::value; }

  Modular &operator+=(const Modular &other) {
    if ((value += other.value) >= mod())
      value -= mod();
    return *this;
  }

  Modular &operator-=(const Modular &other) {
    if ((value -= other.value) < 0)
      value += mod();
    return *this;
  }

  template <typename U> Modular &operator+=(const U &other) {
    return *this += Modular(other);
  }

  template <typename U> Modular &operator-=(const U &other) {
    return *this -= Modular(other);
  }

  Modular &operator++() { return *this += 1; }

  Modular &operator--() { return *this -= 1; }

  Modular operator++(int) {
    Modular result(*this);
    *this += 1;
    return result;
  }

  Modular operator--(int) {
    Modular result(*this);
    *this -= 1;
    return result;
  }

  Modular operator-() const { return Modular(-value); }

  template <typename U = T>
  enable_if_t<is_same<typename Modular<U>::Type, int>::value, Modular> &
  operator*=(const Modular &rhs) {
#ifdef _WIN32
    uint64_t x = static_cast<int64_t>(value) * static_cast<int64_t>(rhs.value);
    uint32_t xh = static_cast<uint32_t>(x >> 32), xl = static_cast<uint32_t>(x),
             d, m;
    asm("divl %4; \n\t" : "=a"(d), "=d"(m) : "d"(xh), "a"(xl), "r"(mod()));
    value = m;
#else
    value = normalize(static_cast<int64_t>(value) *
                      static_cast<int64_t>(rhs.value));
#endif
    return *this;
  }

  template <typename U = T>
  enable_if_t<is_same<typename Modular<U>::Type, int64_t>::value, Modular> &
  operator*=(const Modular &rhs) {
    int64_t q = static_cast<int64_t>(static_cast<long double>(value) *
                                     rhs.value / mod());
    value = normalize(value * rhs.value - q * mod());
    return *this;
  }

  template <typename U = T>
  enable_if_t<!is_integral<typename Modular<U>::Type>::value, Modular> &
  operator*=(const Modular &rhs) {
    value = normalize(value * rhs.value);
    return *this;
  }

  Modular &operator/=(const Modular &other) {
    return *this *= Modular(inverse(other.value, mod()));
  }

  template <typename U> friend const Modular<U> &abs(const Modular<U> &v) {
    return v;
  }

  template <typename U>
  friend bool operator==(const Modular<U> &lhs, const Modular<U> &rhs);

  template <typename U>
  friend bool operator<(const Modular<U> &lhs, const Modular<U> &rhs);

  template <typename U>
  friend std::istream &operator>>(std::istream &stream, Modular<U> &number);

private:
  Type value;
};

template <typename T>
bool operator==(const Modular<T> &lhs, const Modular<T> &rhs) {
  return lhs.value == rhs.value;
}

template <typename T, typename U>
bool operator==(const Modular<T> &lhs, U rhs) {
  return lhs == Modular<T>(rhs);
}

template <typename T, typename U>
bool operator==(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) == rhs;
}

template <typename T>
bool operator!=(const Modular<T> &lhs, const Modular<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T, typename U>
bool operator!=(const Modular<T> &lhs, U rhs) {
  return !(lhs == rhs);
}

template <typename T, typename U>
bool operator!=(U lhs, const Modular<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T>
bool operator<(const Modular<T> &lhs, const Modular<T> &rhs) {
  return lhs.value < rhs.value;
}

template <typename T>
Modular<T> operator+(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T, typename U>
Modular<T> operator+(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T, typename U>
Modular<T> operator+(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T>
Modular<T> operator-(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T, typename U>
Modular<T> operator-(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T, typename U>
Modular<T> operator-(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T>
Modular<T> operator*(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T, typename U>
Modular<T> operator*(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T, typename U>
Modular<T> operator*(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T>
Modular<T> operator/(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> operator/(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> operator/(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> power(const Modular<T> &a, const U &b) {
  assert(b >= 0);
  Modular<T> x = a, res = 1;
  U p = b;
  while (p > 0) {
    if (p & 1)
      res *= x;
    x *= x;
    p >>= 1;
  }
  return res;
}

template <typename T> bool IsZero(const Modular<T> &number) {
  return number() == 0;
}

template <typename T> string to_string(const Modular<T> &number) {
  return to_string(number());
}

template <typename T>
std::ostream &operator<<(std::ostream &stream, const Modular<T> &number) {
  return stream << number();
}

template <typename T>
std::istream &operator>>(std::istream &stream, Modular<T> &number) {
  typename common_type<typename Modular<T>::Type, int64_t>::type x;
  stream >> x;
  number.value = Modular<T>::normalize(x);
  return stream;
}

// debug code by tourist
string to_string(const string &s) { return '"' + s + '"'; }

string to_string(const char *s) { return to_string((string)s); }

string to_string(bool b) { return (b ? "true" : "false"); }

template <typename A, typename B> string to_string(pair<A, B> p) {
  return "(" + to_string(p.first) + ", " + to_string(p.second) + ")";
}

template <typename A> string to_string(A v) {
  bool first = true;
  string res = "{";
  for (const auto &x : v) {
    if (!first) {
      res += ", ";
    }
    first = false;
    res += to_string(x);
  }
  res += "}";
  return res;
}

void debug_out() { cerr << endl; }

template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) {
  cerr << " " << to_string(H);
  debug_out(T...);
}

#if defined LOCAL && !defined DUIPAI
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif

class UnionFind {
  vi par, sz;
  int nTree;

public:
  explicit UnionFind(int n) : par(n), sz(n, 1), nTree(n) { // 0-indexed
    iota(par.begin(), par.end(), 0);
  }

  int n_tree() const { return nTree; }

  int size(int x) { return sz[root(x)]; }

  int root(int x) { return x == par[x] ? x : par[x] = root(par[x]); }

  bool unite(int x, int y) {
    int rx = root(x), ry = root(y);
    if (rx != ry) {
      par[rx] = ry;
      --nTree;
      sz[ry] += sz[rx];
      return true;
    }
    return false;
  }

  bool is_root(int x) const { return par[x] == x; }
  bool same(int x, int y) { return root(x) == root(y); }
};

template <typename T> class BIT {
  using BinOp = function<T(T, T)>;
  BinOp bin_op;
  const T init;
  // 0-indexed
  vector<T> a;

public:
  explicit BIT(
      int n, BinOp op = [](T x, T y) { return x + y; }, T init = T{})
      : bin_op(std::move(op)), init(init), a(n, init) {}

  T prefix(int x) const {
    auto res = init;
    while (x >= 0) {
      res = bin_op(a[x], res);
      x = (x & (x + 1)) - 1;
    }
    return res;
  }

  void modify(int x, T v) {
    while (x < SZ(a)) {
      a[x] = bin_op(a[x], v);
      x |= (x + 1);
    }
  }

  void clear() { fill(a.begin(), a.end(), init); }
};

template <typename int_t> inline int_t lowbit(int_t x) { return x & -x; }

inline unsigned h_bit(unsigned long long x) {
  return sizeof(unsigned long long) * 8 - __builtin_clzll(x);
}

unsigned long long pow2(unsigned long long x) { // round up to power of 2
  return x == lowbit(x) ? x : 1ull << h_bit(x);
}

template <typename T> int get_bit(T a, int i) { return a >> i & 1; }
template <typename T> T get_mid(T l, T r) {
  assert(l <= r);
  return l + (r - l) / 2;
}

#define popcnt(x) __builtin_popcountll((x))
constexpr int md = 1000000007;
using Mint = Modular<std::integral_constant<decay<decltype(md)>::type, md>>;
// ftiasch's AC automaton template
template <int N, int C> struct AhoCorasick {
  AhoCorasick() : n(1) {
    memset(weight, 0, sizeof(weight));
    memset(go, -1, sizeof(go));
  }

  void insert(const char *s, int w = 1) {
    int p = 0;
    for (int i = 0; s[i]; ++i) {
      int c = s[i] - 'a';
      if (go[p][c] == -1) {
        go[p][c] = n++;
      }
      p = go[p][c];
    }
    weight[p] += w;
  }

  void initialize() {
    std::queue<int> queue;
    for (int c = 0; c < C; ++c) {
      int &v = go[0][c];
      if (~v) {
        queue.push(v);
      }
      (~v ? fail[v] : v) = 0;
    }
    while (!queue.empty()) {
      int u = queue.front();
      queue.pop();
      weight[u] += weight[fail[u]];
      for (int c = 0; c < C; ++c) {
        int &v = go[u][c];
        if (~v) {
          queue.push(v);
        }
        (~v ? fail[v] : v) = go[fail[u]][c];
      }
    }
  }

  int n, weight[N]{}, go[N][C]{}, fail[N]{};
};

class GLettersAndQuestionMarks {
  static void SOLVE() {
    const int N = 1001;
    const int M = 14;
    AhoCorasick<N, M> ac;
    int k;
    scan(k);
    int max_length = 0;
    rep(k) {
      string s;
      int c;
      scan(s, c);
      chkmax(max_length, SZ(s));
      ac.insert(s.c_str(), c);
    }
    ac.initialize();
    int n = ac.n;
    debug(n);
    // dp[i][mask] : max value obtained after the first some '?'s determined as
    // specified in mask and arrived at state i of the AC automaton.
    vv<ll> dp(n, vl(1 << M, LLONG_MIN));
    // gain[i]: value gained by traversing the current substring from state i
    vl gain(n);
    // dest[i]: new state reached after traversing the current substring from
    // state i
    vi dest(n);
    iota(all(dest), 0);
    // number of '?' processed so far.
    int m = 0;
    string s;
    scan(s);
    dp[0][0] = 0;
    int sole_dest;
    ll sole_gain;
    int len = 0;
    auto get_dest = [&](int i) {
      return len >= max_length ? sole_dest : dest[i];
    };
    auto get_gain = [&](int i) {
      return len >= max_length ? sole_gain + gain[i] : gain[i];
    };
    FOR(x, s) {
      if (x == '?') {
        rng(mask, 0, 1 << M) {
          if (popcnt(mask) == m) {
            rng(i, 0, n) {
              if (dp[i][mask] != LLONG_MIN) {
                // choose a letter
                rng(c, 0, M) {
                  if (!get_bit(mask, c)) {
                    int j = ac.go[get_dest(i)][c];
                    chkmax(dp[j][mask | 1 << c],
                           dp[i][mask] + get_gain(i) + ac.weight[j]);
                  }
                }
              }
            }
          }
        }
        ++m;
        fill(all(gain), 0);
        iota(all(dest), 0);
        len = 0;
      } else {
        int c = x - 'a';
        ++len;
        if (len < max_length) {
          rng(i, 0, n) {
            dest[i] = ac.go[dest[i]][c];
            gain[i] += ac.weight[dest[i]];
          }
        } else {
          if (len == max_length) {
            sole_dest = dest[0];
            sole_gain = 0;
          }
          sole_dest = ac.go[sole_dest][c];
          sole_gain += ac.weight[sole_dest];
        }
      }
    }
    ll ans = LLONG_MIN;
    rng(mask, 0, 1 << M) {
      if (popcnt(mask) == m) {
        rng(i, 0, n) {
          if (dp[i][mask] != LLONG_MIN) {
            chkmax(ans, dp[i][mask] + get_gain(i));
          }
        }
      }
    }
    println(ans);
//    debug(ac.n);
//    auto output = [&]() {
//      cout << "digraph{\n";
//      rng (i, 0, ac.n) {
//        rng (j, 0, 3) {
//          cout << i << "->" << ac.go[i][j] << " [label=" << char('a' + j)  << "]\n";
//        }
//      }
//      cout << "}";
//    };
//    output();
  }

public:
  static void solve(std::istream &in, std::ostream &out) {
    auto cin_buff = std::cin.rdbuf(in.rdbuf());
    auto cerr_buff = std::cerr.rdbuf(cout.rdbuf(out.rdbuf()));
    SOLVE();
    std::cout.rdbuf(cerr.rdbuf(cerr_buff));
    std::cin.rdbuf(cin_buff);
  }
};

int main() {
  GLettersAndQuestionMarks solver;
  std::istream& in(std::cin);
  std::ostream& out(std::cout);
  solver.solve(in, out);
  return 0;
}


/**
 * code generated by JHelper
 * More info: https://github.com/AlexeyDmitriev/JHelper
 * @author zjsdut
 */

#include <bits/stdc++.h>

using namespace std;

struct fast_ios {
  fast_ios() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
  };
} fast_ios_;

template <typename A, typename B> bool chkmin(A &a, const B &b) {
  if (b < a) {
    a = b;
    return true;
  }
  return false;
}

template <typename A, typename B> bool chkmax(A &a, const B &b) {
  if (b > a) {
    a = b;
    return true;
  }
  return false;
}

template <typename T> istream &operator>>(istream &stream, vector<T> &vec) {
  for (auto &x : vec)
    stream >> x;
  return stream;
}

void scan() {}

template <class T, class... Args> void scan(T &a, Args &... rest) {
  cin >> a;
  scan(rest...);
}

using ll = long long;
using vl = vector<ll>;
using vb = vector<bool>;
using vi = vector<int>;
using pii = pair<int, int>;
template <typename T> using vv = vector<vector<T>>;
#define rng(i, a, b) for (int i = (int)(a); i < (int)(b); ++i)
#define up(i, a, b) for (int i = int(a); i <= int(b); ++i)
#define down(i, b, a) for (int i = int(b); i >= int(a); i--)
#define TOKENPASTE(x, y) x##y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define rep(n)                                                                 \
  for (int TOKENPASTE2(_iter_, __LINE__) = 0,                                  \
                               TOKENPASTE2(_num_, __LINE__) = (int)(n);        \
       TOKENPASTE2(_iter_, __LINE__) < TOKENPASTE2(_num_, __LINE__);           \
       ++TOKENPASTE2(_iter_, __LINE__))
#define FOR(x, cont) for (const auto &x : cont)
#define For(x, cont) for (auto &x : cont)
#define all(x) begin(x), end(x)
#define pb push_back
#define mp make_pair
#define eb emplace_back
#define ep emplace
#define SZ(x) (int)(x).size()
#define UNIQ(vec) (vec).erase(unique(all(vec)), end(vec))
#define LB(cont, x) int(lower_bound(all(cont), x) - begin(cont))
#define UB(cont, x) int(upper_bound(all(cont), x) - begin(cont))

template <typename T>
ostream &operator<<(ostream &stream, const vector<T> &vec) {
  if (!vec.empty()) {
    stream << vec[0];
    for (size_t i = 1; i != vec.size(); ++i)
      stream << ' ' << vec[i];
  }
  return stream;
}

template <typename T> void print(const vector<T> &t) { cout << t << '\n'; }

template <typename T> void print(const vector<vector<T>> &t) {
  for (const auto &row : t) {
    print(row);
  }
}

template <typename T> void print(const T &t) { cout << t << ' '; }

template <typename T, typename... Args>
void print(const T &t, const Args &... rest) {
  print(t);
  print(rest...);
}

template <typename T> void println(const T &t) { cout << t << '\n'; }

template <typename T, typename... Args>
void println(const T &t, const Args &... rest) {
  print(t);
  println(rest...);
}

// tourist's modular-arithmetic class
template <typename T> T inverse(T a, T m) {
  T u = 0, v = 1;
  while (a != 0) {
    T t = m / a;
    m -= t * a;
    swap(a, m);
    u -= t * v;
    swap(u, v);
  }
  assert(m == 1);
  return u;
}

template <typename T> class Modular {
public:
  using Type = typename decay<decltype(T::value)>::type;

  constexpr Modular() : value() {}

  template <typename U> Modular(const U &x) { value = normalize(x); }

  template <typename U> static Type normalize(const U &x) {
    Type v;
    if (-mod() <= x && x < mod())
      v = static_cast<Type>(x);
    else
      v = static_cast<Type>(x % mod());
    if (v < 0)
      v += mod();
    return v;
  }

  const Type &operator()() const { return value; }

  template <typename U> explicit operator U() const {
    return static_cast<U>(value);
  }

  constexpr static Type mod() { return T::value; }

  Modular &operator+=(const Modular &other) {
    if ((value += other.value) >= mod())
      value -= mod();
    return *this;
  }

  Modular &operator-=(const Modular &other) {
    if ((value -= other.value) < 0)
      value += mod();
    return *this;
  }

  template <typename U> Modular &operator+=(const U &other) {
    return *this += Modular(other);
  }

  template <typename U> Modular &operator-=(const U &other) {
    return *this -= Modular(other);
  }

  Modular &operator++() { return *this += 1; }

  Modular &operator--() { return *this -= 1; }

  Modular operator++(int) {
    Modular result(*this);
    *this += 1;
    return result;
  }

  Modular operator--(int) {
    Modular result(*this);
    *this -= 1;
    return result;
  }

  Modular operator-() const { return Modular(-value); }

  template <typename U = T>
  enable_if_t<is_same<typename Modular<U>::Type, int>::value, Modular> &
  operator*=(const Modular &rhs) {
#ifdef _WIN32
    uint64_t x = static_cast<int64_t>(value) * static_cast<int64_t>(rhs.value);
    uint32_t xh = static_cast<uint32_t>(x >> 32), xl = static_cast<uint32_t>(x),
             d, m;
    asm("divl %4; \n\t" : "=a"(d), "=d"(m) : "d"(xh), "a"(xl), "r"(mod()));
    value = m;
#else
    value = normalize(static_cast<int64_t>(value) *
                      static_cast<int64_t>(rhs.value));
#endif
    return *this;
  }

  template <typename U = T>
  enable_if_t<is_same<typename Modular<U>::Type, int64_t>::value, Modular> &
  operator*=(const Modular &rhs) {
    int64_t q = static_cast<int64_t>(static_cast<long double>(value) *
                                     rhs.value / mod());
    value = normalize(value * rhs.value - q * mod());
    return *this;
  }

  template <typename U = T>
  enable_if_t<!is_integral<typename Modular<U>::Type>::value, Modular> &
  operator*=(const Modular &rhs) {
    value = normalize(value * rhs.value);
    return *this;
  }

  Modular &operator/=(const Modular &other) {
    return *this *= Modular(inverse(other.value, mod()));
  }

  template <typename U> friend const Modular<U> &abs(const Modular<U> &v) {
    return v;
  }

  template <typename U>
  friend bool operator==(const Modular<U> &lhs, const Modular<U> &rhs);

  template <typename U>
  friend bool operator<(const Modular<U> &lhs, const Modular<U> &rhs);

  template <typename U>
  friend std::istream &operator>>(std::istream &stream, Modular<U> &number);

private:
  Type value;
};

template <typename T>
bool operator==(const Modular<T> &lhs, const Modular<T> &rhs) {
  return lhs.value == rhs.value;
}

template <typename T, typename U>
bool operator==(const Modular<T> &lhs, U rhs) {
  return lhs == Modular<T>(rhs);
}

template <typename T, typename U>
bool operator==(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) == rhs;
}

template <typename T>
bool operator!=(const Modular<T> &lhs, const Modular<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T, typename U>
bool operator!=(const Modular<T> &lhs, U rhs) {
  return !(lhs == rhs);
}

template <typename T, typename U>
bool operator!=(U lhs, const Modular<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T>
bool operator<(const Modular<T> &lhs, const Modular<T> &rhs) {
  return lhs.value < rhs.value;
}

template <typename T>
Modular<T> operator+(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T, typename U>
Modular<T> operator+(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T, typename U>
Modular<T> operator+(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T>
Modular<T> operator-(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T, typename U>
Modular<T> operator-(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T, typename U>
Modular<T> operator-(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T>
Modular<T> operator*(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T, typename U>
Modular<T> operator*(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T, typename U>
Modular<T> operator*(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T>
Modular<T> operator/(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> operator/(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> operator/(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> power(const Modular<T> &a, const U &b) {
  assert(b >= 0);
  Modular<T> x = a, res = 1;
  U p = b;
  while (p > 0) {
    if (p & 1)
      res *= x;
    x *= x;
    p >>= 1;
  }
  return res;
}

template <typename T> bool IsZero(const Modular<T> &number) {
  return number() == 0;
}

template <typename T> string to_string(const Modular<T> &number) {
  return to_string(number());
}

template <typename T>
std::ostream &operator<<(std::ostream &stream, const Modular<T> &number) {
  return stream << number();
}

template <typename T>
std::istream &operator>>(std::istream &stream, Modular<T> &number) {
  typename common_type<typename Modular<T>::Type, int64_t>::type x;
  stream >> x;
  number.value = Modular<T>::normalize(x);
  return stream;
}

// debug code by tourist
string to_string(const string &s) { return '"' + s + '"'; }

string to_string(const char *s) { return to_string((string)s); }

string to_string(bool b) { return (b ? "true" : "false"); }

template <typename A, typename B> string to_string(pair<A, B> p) {
  return "(" + to_string(p.first) + ", " + to_string(p.second) + ")";
}

template <typename A> string to_string(A v) {
  bool first = true;
  string res = "{";
  for (const auto &x : v) {
    if (!first) {
      res += ", ";
    }
    first = false;
    res += to_string(x);
  }
  res += "}";
  return res;
}

void debug_out() { cerr << endl; }

template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) {
  cerr << " " << to_string(H);
  debug_out(T...);
}

#if defined LOCAL && !defined DUIPAI
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif

class UnionFind {
  vi par, sz;
  int nTree;

public:
  explicit UnionFind(int n) : par(n), sz(n, 1), nTree(n) { // 0-indexed
    iota(par.begin(), par.end(), 0);
  }

  int n_tree() const { return nTree; }

  int size(int x) { return sz[root(x)]; }

  int root(int x) { return x == par[x] ? x : par[x] = root(par[x]); }

  bool unite(int x, int y) {
    int rx = root(x), ry = root(y);
    if (rx != ry) {
      par[rx] = ry;
      --nTree;
      sz[ry] += sz[rx];
      return true;
    }
    return false;
  }

  bool is_root(int x) const { return par[x] == x; }
  bool same(int x, int y) { return root(x) == root(y); }
};

template <typename T> class BIT {
  using BinOp = function<T(T, T)>;
  BinOp bin_op;
  const T init;
  // 0-indexed
  vector<T> a;

public:
  explicit BIT(
      int n, BinOp op = [](T x, T y) { return x + y; }, T init = T{})
      : bin_op(std::move(op)), init(init), a(n, init) {}

  T prefix(int x) const {
    auto res = init;
    while (x >= 0) {
      res = bin_op(a[x], res);
      x = (x & (x + 1)) - 1;
    }
    return res;
  }

  void modify(int x, T v) {
    while (x < SZ(a)) {
      a[x] = bin_op(a[x], v);
      x |= (x + 1);
    }
  }

  void clear() { fill(a.begin(), a.end(), init); }
};

template <typename int_t> inline int_t lowbit(int_t x) { return x & -x; }

inline unsigned h_bit(unsigned long long x) {
  return sizeof(unsigned long long) * 8 - __builtin_clzll(x);
}

unsigned long long pow2(unsigned long long x) { // round up to power of 2
  return x == lowbit(x) ? x : 1ull << h_bit(x);
}

template <typename T> int get_bit(T a, int i) { return a >> i & 1; }
template <typename T> T get_mid(T l, T r) {
  assert(l <= r);
  return l + (r - l) / 2;
}

#define popcnt(x) __builtin_popcountll((x))
constexpr int md = 1000000007;
using Mint = Modular<std::integral_constant<decay<decltype(md)>::type, md>>;
// ftiasch's AC automaton template
template <int N, int C> struct AhoCorasick {
  AhoCorasick() : n(1) {
    memset(weight, 0, sizeof(weight));
    memset(go, -1, sizeof(go));
  }

  void insert(const char *s, int w = 1) {
    int p = 0;
    for (int i = 0; s[i]; ++i) {
      int c = s[i] - 'a';
      if (go[p][c] == -1) {
        go[p][c] = n++;
      }
      p = go[p][c];
    }
    weight[p] += w;
  }

  void initialize() {
    std::queue<int> queue;
    for (int c = 0; c < C; ++c) {
      int &v = go[0][c];
      if (~v) {
        queue.push(v);
      }
      (~v ? fail[v] : v) = 0;
    }
    while (!queue.empty()) {
      int u = queue.front();
      queue.pop();
      weight[u] += weight[fail[u]];
      for (int c = 0; c < C; ++c) {
        int &v = go[u][c];
        if (~v) {
          queue.push(v);
        }
        (~v ? fail[v] : v) = go[fail[u]][c];
      }
    }
  }

  int n, weight[N], go[N][C], fail[N];
};

class GLettersAndQuestionMarks {
  static void SOLVE() {
    const int N = 1001;
    const int M = 14;
    AhoCorasick<N, M> ac;
    int k;
    scan(k);
    rep(k) {
      string s;
      int c;
      scan(s, c);
      ac.insert(s.c_str(), c);
    }
    ac.initialize();
    int n = ac.n;
    // dp[i][mask] : max value obtained after the first some '?'s determined as
    // specified in mask and arrived at state i of the AC automaton.
    vv<ll> dp(n, vl(1 << M, LLONG_MIN));
    // gain[i]: value gained by traversing the current substring from state i
    vl gain(n);
    // dest[i]: new state reached after traversing the current substring from
    // state i
    vi dest(n);
    iota(all(dest), 0);
    // number of '?' processed so far.
    int m = 0;
    string s;
    scan(s);
    dp[0][0] = 0;
    FOR(x, s) {
      if (x == '?') {
        rng(mask, 0, 1 << M) {
          if (popcnt(mask) == m) {
            rng(i, 0, n) {
              if (dp[i][mask] != LLONG_MIN) {
                // choose a letter
                rng(c, 0, M) {
                  if (!get_bit(mask, c)) {
                    int j = ac.go[dest[i]][c];
                    chkmax(dp[j][mask | 1 << c],
                           dp[i][mask] + gain[i] + ac.weight[j]);
                  }
                }
              }
            }
          }
        }
        ++m;
        fill(all(gain), 0);
        iota(all(dest), 0);
      } else {
        int c = x - 'a';
        rng(i, 0, n) {
          dest[i] = ac.go[dest[i]][c];
          gain[i] += ac.weight[dest[i]];
        }
      }
    }
    ll ans = LLONG_MIN;
    rng(mask, 0, 1 << M) {
      if (popcnt(mask) == m) {
        rng(i, 0, n) {
          if (dp[i][mask] != LLONG_MIN) {
            chkmax(ans, dp[i][mask] + gain[i]);
          }
        }
      }
    }
    println(ans);
  }

public:
  static void solve(std::istream &in, std::ostream &out) {
    auto cin_buff = std::cin.rdbuf(in.rdbuf());
    auto cerr_buff = std::cerr.rdbuf(cout.rdbuf(out.rdbuf()));
    SOLVE();
    std::cout.rdbuf(cerr.rdbuf(cerr_buff));
    std::cin.rdbuf(cin_buff);
  }
};

int main() {
  GLettersAndQuestionMarks solver;
  std::istream& in(std::cin);
  std::ostream& out(std::cout);
  solver.solve(in, out);
  return 0;
}


/**
 * code generated by JHelper
 * More info: https://github.com/AlexeyDmitriev/JHelper
 * @author zjsdut
 */

#include <bits/stdc++.h>

using namespace std;

struct fast_ios {
  fast_ios() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);
  };
} fast_ios_;

template <typename A, typename B> bool chkmin(A &a, const B &b) {
  if (b < a) {
    a = b;
    return true;
  }
  return false;
}

template <typename A, typename B> bool chkmax(A &a, const B &b) {
  if (b > a) {
    a = b;
    return true;
  }
  return false;
}

template <typename T> istream &operator>>(istream &stream, vector<T> &vec) {
  for (auto &x : vec)
    stream >> x;
  return stream;
}

void scan() {}

template <class T, class... Args> void scan(T &a, Args &... rest) {
  cin >> a;
  scan(rest...);
}

using ll = long long;
using vl = vector<ll>;
using vb = vector<bool>;
using vi = vector<int>;
using pii = pair<int, int>;
template <typename T> using vv = vector<vector<T>>;
#define rng(i, a, b) for (int i = (int)(a); i < (int)(b); ++i)
#define up(i, a, b) for (int i = int(a); i <= int(b); ++i)
#define down(i, b, a) for (int i = int(b); i >= int(a); i--)
#define TOKENPASTE(x, y) x##y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define rep(n)                                                                 \
  for (int TOKENPASTE2(_iter_, __LINE__) = 0,                                  \
                               TOKENPASTE2(_num_, __LINE__) = (int)(n);        \
       TOKENPASTE2(_iter_, __LINE__) < TOKENPASTE2(_num_, __LINE__);           \
       ++TOKENPASTE2(_iter_, __LINE__))
#define FOR(x, cont) for (const auto &x : cont)
#define For(x, cont) for (auto &x : cont)
#define all(x) begin(x), end(x)
#define pb push_back
#define mp make_pair
#define eb emplace_back
#define ep emplace
#define SZ(x) (int)(x).size()
#define UNIQ(vec) (vec).erase(unique(all(vec)), end(vec))
#define LB(cont, x) int(lower_bound(all(cont), x) - begin(cont))
#define UB(cont, x) int(upper_bound(all(cont), x) - begin(cont))

template <typename T>
ostream &operator<<(ostream &stream, const vector<T> &vec) {
  if (!vec.empty()) {
    stream << vec[0];
    for (size_t i = 1; i != vec.size(); ++i)
      stream << ' ' << vec[i];
  }
  return stream;
}

template <typename T> void print(const vector<T> &t) { cout << t << '\n'; }

template <typename T> void print(const vector<vector<T>> &t) {
  for (const auto &row : t) {
    print(row);
  }
}

template <typename T> void print(const T &t) { cout << t << ' '; }

template <typename T, typename... Args>
void print(const T &t, const Args &... rest) {
  print(t);
  print(rest...);
}

template <typename T> void println(const T &t) { cout << t << '\n'; }

template <typename T, typename... Args>
void println(const T &t, const Args &... rest) {
  print(t);
  println(rest...);
}

// tourist's modular-arithmetic class
template <typename T> T inverse(T a, T m) {
  T u = 0, v = 1;
  while (a != 0) {
    T t = m / a;
    m -= t * a;
    swap(a, m);
    u -= t * v;
    swap(u, v);
  }
  assert(m == 1);
  return u;
}

template <typename T> class Modular {
public:
  using Type = typename decay<decltype(T::value)>::type;

  constexpr Modular() : value() {}

  template <typename U> Modular(const U &x) { value = normalize(x); }

  template <typename U> static Type normalize(const U &x) {
    Type v;
    if (-mod() <= x && x < mod())
      v = static_cast<Type>(x);
    else
      v = static_cast<Type>(x % mod());
    if (v < 0)
      v += mod();
    return v;
  }

  const Type &operator()() const { return value; }

  template <typename U> explicit operator U() const {
    return static_cast<U>(value);
  }

  constexpr static Type mod() { return T::value; }

  Modular &operator+=(const Modular &other) {
    if ((value += other.value) >= mod())
      value -= mod();
    return *this;
  }

  Modular &operator-=(const Modular &other) {
    if ((value -= other.value) < 0)
      value += mod();
    return *this;
  }

  template <typename U> Modular &operator+=(const U &other) {
    return *this += Modular(other);
  }

  template <typename U> Modular &operator-=(const U &other) {
    return *this -= Modular(other);
  }

  Modular &operator++() { return *this += 1; }

  Modular &operator--() { return *this -= 1; }

  Modular operator++(int) {
    Modular result(*this);
    *this += 1;
    return result;
  }

  Modular operator--(int) {
    Modular result(*this);
    *this -= 1;
    return result;
  }

  Modular operator-() const { return Modular(-value); }

  template <typename U = T>
  enable_if_t<is_same<typename Modular<U>::Type, int>::value, Modular> &
  operator*=(const Modular &rhs) {
#ifdef _WIN32
    uint64_t x = static_cast<int64_t>(value) * static_cast<int64_t>(rhs.value);
    uint32_t xh = static_cast<uint32_t>(x >> 32), xl = static_cast<uint32_t>(x),
             d, m;
    asm("divl %4; \n\t" : "=a"(d), "=d"(m) : "d"(xh), "a"(xl), "r"(mod()));
    value = m;
#else
    value = normalize(static_cast<int64_t>(value) *
                      static_cast<int64_t>(rhs.value));
#endif
    return *this;
  }

  template <typename U = T>
  enable_if_t<is_same<typename Modular<U>::Type, int64_t>::value, Modular> &
  operator*=(const Modular &rhs) {
    int64_t q = static_cast<int64_t>(static_cast<long double>(value) *
                                     rhs.value / mod());
    value = normalize(value * rhs.value - q * mod());
    return *this;
  }

  template <typename U = T>
  enable_if_t<!is_integral<typename Modular<U>::Type>::value, Modular> &
  operator*=(const Modular &rhs) {
    value = normalize(value * rhs.value);
    return *this;
  }

  Modular &operator/=(const Modular &other) {
    return *this *= Modular(inverse(other.value, mod()));
  }

  template <typename U> friend const Modular<U> &abs(const Modular<U> &v) {
    return v;
  }

  template <typename U>
  friend bool operator==(const Modular<U> &lhs, const Modular<U> &rhs);

  template <typename U>
  friend bool operator<(const Modular<U> &lhs, const Modular<U> &rhs);

  template <typename U>
  friend std::istream &operator>>(std::istream &stream, Modular<U> &number);

private:
  Type value;
};

template <typename T>
bool operator==(const Modular<T> &lhs, const Modular<T> &rhs) {
  return lhs.value == rhs.value;
}

template <typename T, typename U>
bool operator==(const Modular<T> &lhs, U rhs) {
  return lhs == Modular<T>(rhs);
}

template <typename T, typename U>
bool operator==(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) == rhs;
}

template <typename T>
bool operator!=(const Modular<T> &lhs, const Modular<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T, typename U>
bool operator!=(const Modular<T> &lhs, U rhs) {
  return !(lhs == rhs);
}

template <typename T, typename U>
bool operator!=(U lhs, const Modular<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T>
bool operator<(const Modular<T> &lhs, const Modular<T> &rhs) {
  return lhs.value < rhs.value;
}

template <typename T>
Modular<T> operator+(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T, typename U>
Modular<T> operator+(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T, typename U>
Modular<T> operator+(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) += rhs;
}

template <typename T>
Modular<T> operator-(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T, typename U>
Modular<T> operator-(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T, typename U>
Modular<T> operator-(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) -= rhs;
}

template <typename T>
Modular<T> operator*(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T, typename U>
Modular<T> operator*(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T, typename U>
Modular<T> operator*(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) *= rhs;
}

template <typename T>
Modular<T> operator/(const Modular<T> &lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> operator/(const Modular<T> &lhs, U rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> operator/(U lhs, const Modular<T> &rhs) {
  return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> power(const Modular<T> &a, const U &b) {
  assert(b >= 0);
  Modular<T> x = a, res = 1;
  U p = b;
  while (p > 0) {
    if (p & 1)
      res *= x;
    x *= x;
    p >>= 1;
  }
  return res;
}

template <typename T> bool IsZero(const Modular<T> &number) {
  return number() == 0;
}

template <typename T> string to_string(const Modular<T> &number) {
  return to_string(number());
}

template <typename T>
std::ostream &operator<<(std::ostream &stream, const Modular<T> &number) {
  return stream << number();
}

template <typename T>
std::istream &operator>>(std::istream &stream, Modular<T> &number) {
  typename common_type<typename Modular<T>::Type, int64_t>::type x;
  stream >> x;
  number.value = Modular<T>::normalize(x);
  return stream;
}

// debug code by tourist
string to_string(const string &s) { return '"' + s + '"'; }

string to_string(const char *s) { return to_string((string)s); }

string to_string(bool b) { return (b ? "true" : "false"); }

template <typename A, typename B> string to_string(pair<A, B> p) {
  return "(" + to_string(p.first) + ", " + to_string(p.second) + ")";
}

template <typename A> string to_string(A v) {
  bool first = true;
  string res = "{";
  for (const auto &x : v) {
    if (!first) {
      res += ", ";
    }
    first = false;
    res += to_string(x);
  }
  res += "}";
  return res;
}

void debug_out() { cerr << endl; }

template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) {
  cerr << " " << to_string(H);
  debug_out(T...);
}

#if defined LOCAL && !defined DUIPAI
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif

class UnionFind {
  vi par, sz;
  int nTree;

public:
  explicit UnionFind(int n) : par(n), sz(n, 1), nTree(n) { // 0-indexed
    iota(par.begin(), par.end(), 0);
  }

  int n_tree() const { return nTree; }

  int size(int x) { return sz[root(x)]; }

  int root(int x) { return x == par[x] ? x : par[x] = root(par[x]); }

  bool unite(int x, int y) {
    int rx = root(x), ry = root(y);
    if (rx != ry) {
      par[rx] = ry;
      --nTree;
      sz[ry] += sz[rx];
      return true;
    }
    return false;
  }

  bool is_root(int x) const { return par[x] == x; }
  bool same(int x, int y) { return root(x) == root(y); }
};

template <typename T> class BIT {
  using BinOp = function<T(T, T)>;
  BinOp bin_op;
  const T init;
  // 0-indexed
  vector<T> a;

public:
  explicit BIT(
      int n, BinOp op = [](T x, T y) { return x + y; }, T init = T{})
      : bin_op(std::move(op)), init(init), a(n, init) {}

  T prefix(int x) const {
    auto res = init;
    while (x >= 0) {
      res = bin_op(a[x], res);
      x = (x & (x + 1)) - 1;
    }
    return res;
  }

  void modify(int x, T v) {
    while (x < SZ(a)) {
      a[x] = bin_op(a[x], v);
      x |= (x + 1);
    }
  }

  void clear() { fill(a.begin(), a.end(), init); }
};

template <typename int_t> inline int_t lowbit(int_t x) { return x & -x; }

inline unsigned h_bit(unsigned long long x) {
  return sizeof(unsigned long long) * 8 - __builtin_clzll(x);
}

unsigned long long pow2(unsigned long long x) { // round up to power of 2
  return x == lowbit(x) ? x : 1ull << h_bit(x);
}

template <typename T> int get_bit(T a, int i) { return a >> i & 1; }
template <typename T> T get_mid(T l, T r) {
  assert(l <= r);
  return l + (r - l) / 2;
}

#define popcnt(x) __builtin_popcountll((x))
constexpr int md = 1000000007;
using Mint = Modular<std::integral_constant<decay<decltype(md)>::type, md>>;
// ftiasch's AC automaton template
template <int N, int C> struct AhoCorasick {
  AhoCorasick() : n(1) {
    memset(weight, 0, sizeof(weight));
    memset(go, -1, sizeof(go));
  }

  void insert(const char *s, int w = 1) {
    int p = 0;
    for (int i = 0; s[i]; ++i) {
      int c = s[i] - 'a';
      if (go[p][c] == -1) {
        go[p][c] = n++;
      }
      p = go[p][c];
    }
    weight[p] += w;
  }

  void initialize() {
    std::queue<int> queue;
    for (int c = 0; c < C; ++c) {
      int &v = go[0][c];
      if (~v) {
        queue.push(v);
      }
      (~v ? fail[v] : v) = 0;
    }
    while (!queue.empty()) {
      int u = queue.front();
      queue.pop();
      weight[u] += weight[fail[u]];
      for (int c = 0; c < C; ++c) {
        int &v = go[u][c];
        if (~v) {
          queue.push(v);
        }
        (~v ? fail[v] : v) = go[fail[u]][c];
      }
    }
  }

  int n, weight[N], go[N][C], fail[N];
};

class GLettersAndQuestionMarks {
  static void SOLVE() {
    const int N = 1001;
    const int M = 14;
    AhoCorasick<N, M> ac;
    int k;
    scan(k);
    rep(k) {
      string s;
      int c;
      scan(s, c);
      ac.insert(s.c_str(), c);
    }
    ac.initialize();
    int n = ac.n;
    // dp[i][mask] : max value obtained after the first some '?'s determined as
    // specified in mask and arrived at state i of AC automaton.
    vv<ll> dp(n, vl(1 << M, LLONG_MIN));
    // gain[i]: value gained by traversing the current substring from state i
    vl gain(n);
    // dest[i]: new state after traversing the current substring from state i
    vi dest(n);
    iota(all(dest), 0);
    // number of '?' processed so far.
    int m = 0;
    string s;
    scan(s);
    dp[0][0] = 0;
    FOR(x, s) {
      if (x == '?') {
        // choose a letter
        rng(mask, 0, 1 << M) {
          if (popcnt(mask) == m) {
            rng(c, 0, M) {
              if (!get_bit(mask, c)) {
                int nmask = mask | 1 << c;
                rng(i, 0, n) {
                  if (dp[i][mask] != LLONG_MIN) {
                    int ni = ac.go[dest[i]][c];
                    chkmax(dp[ni][nmask],
                           dp[i][mask] + gain[i] + ac.weight[ni]);
                  }
                }
              }
            }
          }
        }
        ++m;
        fill(all(gain), 0);
        iota(all(dest), 0);
      } else {
        int c = x - 'a';
        rng(i, 0, n) {
          dest[i] = ac.go[dest[i]][c];
          gain[i] += ac.weight[dest[i]];
        }
      }
    }
    ll ans = LLONG_MIN;
    rng(mask, 0, 1 << M) {
      if (popcnt(mask) == m) {
        rng(i, 0, n) {
          if (dp[i][mask] != LLONG_MIN) {
            chkmax(ans, dp[i][mask] + gain[i]);
          }
        }
      }
    }
    println(ans);
  }

public:
  static void solve(std::istream &in, std::ostream &out) {
    auto cin_buff = std::cin.rdbuf(in.rdbuf());
    auto cerr_buff = std::cerr.rdbuf(cout.rdbuf(out.rdbuf()));
    SOLVE();
    std::cout.rdbuf(cerr.rdbuf(cerr_buff));
    std::cin.rdbuf(cin_buff);
  }
};

int main() {
  GLettersAndQuestionMarks solver;
  std::istream& in(std::cin);
  std::ostream& out(std::cout);
  solver.solve(in, out);
  return 0;
}


#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace chrono;
using namespace __gnu_pbds;
mt19937 rng(high_resolution_clock::now().time_since_epoch().count());
mt19937_64 rngll(high_resolution_clock::now().time_since_epoch().count());
template<typename T> T ctmax(T &x, const T &y){ return x = max(x, y); }
template<typename T> T ctmin(T &x, const T &y){ return x = min(x, y); }
template<typename T> using Tree = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

template<typename Str, int lim = 128>
struct aho_corasic{
	typedef typename Str::value_type Char;
	struct node{
		int par, link = -1, elink = -1;
		Char cpar;
		vector<int> next, go;
		bool isleaf = false;
		node(int par = -1, Char pch = '$'): par(par), cpar(pch), next(128, -1), go(128, -1){ }
		long long val = 0;
	};
	vector<node> state = vector<node>(1);
	int insert(const Str &s){
		int u = 0;
		for(auto &c: s){
			if(state[u].next[c] == -1){
				state[u].next[c] = int(state.size());
				state.emplace_back(u, c);
			}
			u = state[u].next[c];
		}
		state[u].isleaf = true;
		return u;
	}
	int get_link(int u){
		if(state[u].link == -1){
			if(!u || !state[u].par) state[u].link = 0;
			else state[u].link = go(get_link(state[u].par), state[u].cpar);
		}
		return state[u].link;
	}
	int get_elink(int u){
		if(state[u].elink == -1){
			if(!u || !get_link(u)) state[u].elink = 0;
			else if(state[get_link(u)].isleaf) state[u].elink = get_link(u);
			else state[u].elink = get_elink(get_link(u));
		}
		return state[u].elink;
	}
	int go(int u, Char c){
		if(state[u].go[c] == -1){
			if(state[u].next[c] != -1) state[u].go[c] = state[u].next[c];
			else state[u].go[c] = u ? go(get_link(u), c) : u;
		}
		return state[u].go[c];
	}
	void print(int u, Str s = ""){
		cout << "Node " << u << ": par = " << state[u].par << ", cpar = " << state[u].cpar << ", string: " << s << "\n";
		for(int c = 0; c < lim; ++ c){
			if(state[u].next[c] != -1){
				cout << u << " => ";
				print(state[u].next[c], s + Str{c});
			}
		}
	}
};

int main(){
	cin.tie(0)->sync_with_stdio(0);
	aho_corasic<string> aut;
	int k;
	cin >> k;
	for(int i = 0; i < k; ++ i){
		string t;
		int x;
		cin >> t >> x;
		int u = aut.insert(t);
		aut.state[u].val += x;
	}
	int n = int(aut.state.size());
	for(int u = 0; u < n; ++ u){
		if(aut.state[u].isleaf){
			aut.state[u].val += aut.state[aut.get_elink(u)].val;
		}
	}
	const int mx = 14;
	string s;
	cin >> s;
	vector<vector<int>> masks(mx + 1);
	for(int mask = 0; mask < 1 << mx; ++ mask){
		masks[__builtin_popcount(mask)].push_back(mask);
	}
	const long long minf = numeric_limits<long long>::min() / 8;
	vector<vector<long long>> dp(n, vector<long long>(1 << mx, minf));
	dp[0][0] = 0;
	for(int i = 0; i < int(s.size()); ){
		static int cur_bit = 0;
		int j = i;
		while(j < int(s.size()) && s[j] != '?'){
			++ j;
		}
		static vector<vector<long long>> dp_next(n, vector<long long>(1 << mx));
		auto init_next = [&](){
			for(int i = 0; i < n; ++ i){
				for(int j = 0; j < 1 << mx; ++ j){
					dp_next[i][j] = minf;
				}
			}
		};
		init_next();
		for(int u = 0; u < n; ++ u){
			long long sum = 0;
			int v = u;
			for(int k = i; k < j; ++ k){
				v = aut.go(v, s[k]);
				sum += aut.state[v].val;
			}
			for(auto mask: masks[cur_bit]){
				if(dp[u][mask] != minf){
					ctmax(dp_next[v][mask], dp[u][mask] + sum);
				}
			}
		}
		swap(dp, dp_next);
		if(j < int(s.size())){
			init_next();
			for(auto mask: masks[cur_bit]){
				for(int bit = 0; bit < mx; ++ bit){
					if(!(mask & 1 << bit)){
						int vmask = mask | 1 << bit;
						for(int u = 0; u < n; ++ u){
							if(dp[u][mask] != minf){
								int v = aut.go(u, 'a' + bit);
								ctmax(dp_next[v][vmask], dp[u][mask] + aut.state[v].val);
							}
						}
					}
				}
			}
			swap(dp, dp_next);
			++ cur_bit;
		}
		i = j + 1;
	}
	long long res = minf;
	for(int u = 0; u < n; ++ u){
		ctmax(res, *max_element(dp[u].begin(), dp[u].end()));
	}
	cout << res;
	return 0;
}

/*

*/

////////////////////////////////////////////////////////////////////////////////////////
//                                                                                //
//                                   Coded by Aeren                                   //
//                                                                                //
////////////////////////////////////////////////////////////////////////////////////////


#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace chrono;
using namespace __gnu_pbds;
mt19937 rng(high_resolution_clock::now().time_since_epoch().count());
mt19937_64 rngll(high_resolution_clock::now().time_since_epoch().count());
template<typename T> T ctmax(T &x, const T &y){ return x = max(x, y); }
template<typename T> T ctmin(T &x, const T &y){ return x = min(x, y); }
template<typename T> using Tree = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

template<typename Str, int lim = 128>
struct aho_corasic{
	typedef typename Str::value_type Char;
	struct node{
		int par, link = -1, elink = -1;
		Char cpar;
		vector<int> next, go;
		bool isleaf = false;
		node(int par = -1, Char pch = '$'): par(par), cpar(pch), next(128, -1), go(128, -1){ }
		long long val = 0;
	};
	vector<node> state = vector<node>(1);
	int insert(const Str &s){
		int u = 0;
		for(auto &c: s){
			if(state[u].next[c] == -1){
				state[u].next[c] = int(state.size());
				state.emplace_back(u, c);
			}
			u = state[u].next[c];
		}
		state[u].isleaf = true;
		return u;
	}
	int get_link(int u){
		if(state[u].link == -1){
			if(!u || !state[u].par) state[u].link = 0;
			else state[u].link = go(get_link(state[u].par), state[u].cpar);
		}
		return state[u].link;
	}
	int get_elink(int u){
		if(state[u].elink == -1){
			if(!u || !get_link(u)) state[u].elink = 0;
			else if(state[get_link(u)].isleaf) state[u].elink = get_link(u);
			else state[u].elink = get_elink(get_link(u));
		}
		return state[u].elink;
	}
	int go(int u, Char c){
		if(state[u].go[c] == -1){
			if(state[u].next[c] != -1) state[u].go[c] = state[u].next[c];
			else state[u].go[c] = u ? go(get_link(u), c) : u;
		}
		return state[u].go[c];
	}
	void print(int u, Str s = ""){
		cout << "Node " << u << ": par = " << state[u].par << ", cpar = " << state[u].cpar << ", string: " << s << "\n";
		for(int c = 0; c < lim; ++ c){
			if(state[u].next[c] != -1){
				cout << u << " => ";
				print(state[u].next[c], s + Str{c});
			}
		}
	}
};

int main(){
	cin.tie(0)->sync_with_stdio(0);
	aho_corasic<string> aut;
	int k;
	cin >> k;
	for(int i = 0; i < k; ++ i){
		string t;
		int x;
		cin >> t >> x;
		int u = aut.insert(t);
		aut.state[u].val += x;
	}
	int n = int(aut.state.size());
	for(int u = 1; u < n; ++ u){
		aut.state[u].val += aut.state[aut.get_elink(u)].val;
	}
	const int mx = 14;
	string s;
	cin >> s;
	vector<vector<int>> masks(mx + 1);
	for(int mask = 0; mask < 1 << mx; ++ mask){
		masks[__builtin_popcount(mask)].push_back(mask);
	}
	const long long minf = numeric_limits<long long>::min() / 8;
	vector<vector<long long>> dp(n, vector<long long>(1 << mx, minf));
	dp[0][0] = 0;
	for(int i = 0; i < int(s.size()); ){
		static int cur_bit = 0;
		int j = i;
		while(j < int(s.size()) && s[j] != '?'){
			++ j;
		}
		static vector<vector<long long>> dp_next(n, vector<long long>(1 << mx));
		auto init_next = [&](){
			for(int i = 0; i < n; ++ i){
				for(int j = 0; j < 1 << mx; ++ j){
					dp_next[i][j] = minf;
				}
			}
		};
		init_next();
		for(int u = 0; u < n; ++ u){
			long long sum = 0;
			int v = u;
			for(int k = i; k < j; ++ k){
				v = aut.go(v, s[k]);
				sum += aut.state[v].val;
			}
			for(auto mask: masks[cur_bit]){
				if(dp[u][mask] != minf){
					ctmax(dp_next[v][mask], dp[u][mask] + sum);
				}
			}
		}
		swap(dp, dp_next);
		if(j < int(s.size())){
			init_next();
			for(auto mask: masks[cur_bit]){
				for(int bit = 0; bit < mx; ++ bit){
					if(!(mask & 1 << bit)){
						int vmask = mask | 1 << bit;
						for(int u = 0; u < n; ++ u){
							if(dp[u][mask] != minf){
								int v = aut.go(u, 'a' + bit);
								ctmax(dp_next[v][vmask], dp[u][mask] + aut.state[v].val);
							}
						}
					}
				}
			}
			swap(dp, dp_next);
			++ cur_bit;
		}
		i = j + 1;
	}
	long long res = minf;
	for(int u = 0; u < n; ++ u){
		ctmax(res, *max_element(dp[u].begin(), dp[u].end()));
	}
	cout << res;
	return 0;
}

/*

*/

////////////////////////////////////////////////////////////////////////////////////////
//                                                                                //
//                                   Coded by Aeren                                   //
//                                                                                //
////////////////////////////////////////////////////////////////////////////////////////


#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace chrono;
using namespace __gnu_pbds;
mt19937 rng(high_resolution_clock::now().time_since_epoch().count());
mt19937_64 rngll(high_resolution_clock::now().time_since_epoch().count());
template<typename T> T ctmax(T &x, const T &y){ return x = max(x, y); }
template<typename T> T ctmin(T &x, const T &y){ return x = min(x, y); }
template<typename T> using Tree = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

template<typename Str, int lim = 128>
struct aho_corasic{
	typedef typename Str::value_type Char;
	struct node{
		int par, link = -1, elink = -1;
		Char cpar;
		vector<int> next, go;
		bool isleaf = false;
		node(int par = -1, Char pch = '$'): par(par), cpar(pch), next(128, -1), go(128, -1){ }
		long long val = 0;
	};
	vector<node> state = vector<node>(1);
	int insert(const Str &s){
		int u = 0;
		for(auto &c: s){
			if(state[u].next[c] == -1){
				state[u].next[c] = int(state.size());
				state.emplace_back(u, c);
			}
			u = state[u].next[c];
		}
		state[u].isleaf = true;
		return u;
	}
	int get_link(int u){
		if(state[u].link == -1){
			if(!u || !state[u].par) state[u].link = 0;
			else state[u].link = go(get_link(state[u].par), state[u].cpar);
		}
		return state[u].link;
	}
	int get_elink(int u){
		if(state[u].elink == -1){
			if(!u || !get_link(u)) state[u].elink = 0;
			else if(state[get_link(u)].isleaf) state[u].elink = get_link(u);
			else state[u].elink = get_elink(get_link(u));
		}
		return state[u].elink;
	}
	int go(int u, Char c){
		if(state[u].go[c] == -1){
			if(state[u].next[c] != -1) state[u].go[c] = state[u].next[c];
			else state[u].go[c] = u ? go(get_link(u), c) : u;
		}
		return state[u].go[c];
	}
	void print(int u, Str s = ""){
		cout << "Node " << u << ": par = " << state[u].par << ", cpar = " << state[u].cpar << ", string: " << s << "\n";
		for(int c = 0; c < lim; ++ c){
			if(state[u].next[c] != -1){
				cout << u << " => ";
				print(state[u].next[c], s + Str{c});
			}
		}
	}
};

int main(){
	cin.tie(0)->sync_with_stdio(0);
	aho_corasic<string> aut;
	int k;
	cin >> k;
	for(int i = 0; i < k; ++ i){
		string t;
		int x;
		cin >> t >> x;
		int u = aut.insert(t);
		aut.state[u].val += x;
	}
	int n = int(aut.state.size());
	for(int u = 0; u < n; ++ u){
		aut.state[u].val += aut.state[aut.get_elink(u)].val;
	}
	const int mx = 14;
	string s;
	cin >> s;
	vector<vector<int>> masks(mx + 1);
	for(int mask = 0; mask < 1 << mx; ++ mask){
		masks[__builtin_popcount(mask)].push_back(mask);
	}
	const long long minf = numeric_limits<long long>::min() / 8;
	vector<vector<long long>> dp(n, vector<long long>(1 << mx, minf));
	dp[0][0] = 0;
	for(int i = 0; i < int(s.size()); ){
		static int cur_bit = 0;
		int j = i;
		while(j < int(s.size()) && s[j] != '?'){
			++ j;
		}
		static vector<vector<long long>> dp_next(n, vector<long long>(1 << mx));
		auto init_next = [&](){
			for(int i = 0; i < n; ++ i){
				for(int j = 0; j < 1 << mx; ++ j){
					dp_next[i][j] = minf;
				}
			}
		};
		init_next();
		for(int u = 0; u < n; ++ u){
			long long sum = 0;
			int v = u;
			for(int k = i; k < j; ++ k){
				v = aut.go(v, s[k]);
				sum += aut.state[v].val;
			}
			for(auto mask: masks[cur_bit]){
				if(dp[u][mask] != minf){
					ctmax(dp_next[v][mask], dp[u][mask] + sum);
				}
			}
		}
		swap(dp, dp_next);
		if(j < int(s.size())){
			init_next();
			for(auto mask: masks[cur_bit]){
				for(int bit = 0; bit < mx; ++ bit){
					if(!(mask & 1 << bit)){
						int vmask = mask | 1 << bit;
						for(int u = 0; u < n; ++ u){
							if(dp[u][mask] != minf){
								int v = aut.go(u, 'a' + bit);
								ctmax(dp_next[v][vmask], dp[u][mask] + aut.state[v].val);
							}
						}
					}
				}
			}
			swap(dp, dp_next);
			++ cur_bit;
		}
		i = j + 1;
	}
	long long res = minf;
	for(int u = 0; u < n; ++ u){
		ctmax(res, *max_element(dp[u].begin(), dp[u].end()));
	}
	cout << res;
	return 0;
}

/*

*/

////////////////////////////////////////////////////////////////////////////////////////
//                                                                                //
//                                   Coded by Aeren                                   //
//                                                                                //
////////////////////////////////////////////////////////////////////////////////////////


#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace chrono;
using namespace __gnu_pbds;
mt19937 rng(high_resolution_clock::now().time_since_epoch().count());
mt19937_64 rngll(high_resolution_clock::now().time_since_epoch().count());
template<typename T> T ctmax(T &x, const T &y){ return x = max(x, y); }
template<typename T> T ctmin(T &x, const T &y){ return x = min(x, y); }
template<typename T> using Tree = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

template<typename Str, int lim = 128>
struct aho_corasic{
	typedef typename Str::value_type Char;
	struct node{
		int par, link = -1, elink = -1;
		Char cpar;
		vector<int> next, go;
		bool isleaf = false;
		node(int par = -1, Char pch = '$'): par(par), cpar(pch), next(128, -1), go(128, -1){ }
		long long val = 0;
		bool mark = false;
	};
	vector<node> state = vector<node>(1);
	int insert(const Str &s){
		int u = 0;
		for(auto &c: s){
			if(state[u].next[c] == -1){
				state[u].next[c] = int(state.size());
				state.emplace_back(u, c);
			}
			u = state[u].next[c];
		}
		state[u].isleaf = true;
		return u;
	}
	int get_link(int u){
		if(state[u].link == -1){
			if(!u || !state[u].par) state[u].link = 0;
			else state[u].link = go(get_link(state[u].par), state[u].cpar);
		}
		return state[u].link;
	}
	int get_elink(int u){
		if(state[u].elink == -1){
			if(!u || !get_link(u)) state[u].elink = 0;
			else if(state[get_link(u)].isleaf) state[u].elink = get_link(u);
			else state[u].elink = get_elink(get_link(u));
		}
		return state[u].elink;
	}
	int go(int u, Char c){
		if(state[u].go[c] == -1){
			if(state[u].next[c] != -1) state[u].go[c] = state[u].next[c];
			else state[u].go[c] = u ? go(get_link(u), c) : u;
		}
		return state[u].go[c];
	}
	void print(int u, Str s = ""){
		cout << "Node " << u << ": par = " << state[u].par << ", cpar = " << state[u].cpar << ", string: " << s << "\n";
		for(int c = 0; c < lim; ++ c){
			if(state[u].next[c] != -1){
				cout << u << " => ";
				print(state[u].next[c], s + Str{c});
			}
		}
	}
};

int main(){
	cin.tie(0)->sync_with_stdio(0);
	aho_corasic<string> aut;
	int k;
	cin >> k;
	for(int i = 0; i < k; ++ i){
		string t;
		int x;
		cin >> t >> x;
		int u = aut.insert(t);
		aut.state[u].val += x;
	}
	int n = int(aut.state.size());
	function<long long(int)> process = [&](int u){
		if(!u){
			return 0LL;
		}
		aut.state[u].mark = true;
		return aut.state[u].val += process(aut.get_elink(u));
	};
	for(int u = 0; u < n; ++ u){
		if(!aut.state[u].mark){
			process(u);
		}
	}
	const int mx = 14;
	string s;
	cin >> s;
	vector<vector<int>> masks(mx + 1);
	for(int mask = 0; mask < 1 << mx; ++ mask){
		masks[__builtin_popcount(mask)].push_back(mask);
	}
	const long long minf = numeric_limits<long long>::min() / 8;
	vector<vector<long long>> dp(n, vector<long long>(1 << mx, minf));
	dp[0][0] = 0;
	for(int i = 0; i < int(s.size()); ){
		static int cur_bit = 0;
		int j = i;
		while(j < int(s.size()) && s[j] != '?'){
			++ j;
		}
		static vector<vector<long long>> dp_next(n, vector<long long>(1 << mx));
		auto init_next = [&](){
			for(int i = 0; i < n; ++ i){
				for(int j = 0; j < 1 << mx; ++ j){
					dp_next[i][j] = minf;
				}
			}
		};
		init_next();
		for(int u = 0; u < n; ++ u){
			long long sum = 0;
			int v = u;
			for(int k = i; k < j; ++ k){
				v = aut.go(v, s[k]);
				sum += aut.state[v].val;
			}
			for(auto mask: masks[cur_bit]){
				if(dp[u][mask] != minf){
					ctmax(dp_next[v][mask], dp[u][mask] + sum);
				}
			}
		}
		swap(dp, dp_next);
		if(j < int(s.size())){
			init_next();
			for(auto mask: masks[cur_bit]){
				for(int bit = 0; bit < mx; ++ bit){
					if(!(mask & 1 << bit)){
						int vmask = mask | 1 << bit;
						for(int u = 0; u < n; ++ u){
							if(dp[u][mask] != minf){
								int v = aut.go(u, 'a' + bit);
								ctmax(dp_next[v][vmask], dp[u][mask] + aut.state[v].val);
							}
						}
					}
				}
			}
			swap(dp, dp_next);
			++ cur_bit;
		}
		i = j + 1;
	}
	long long res = minf;
	for(int u = 0; u < n; ++ u){
		ctmax(res, *max_element(dp[u].begin(), dp[u].end()));
	}
	cout << res;
	return 0;
}

/*

*/

////////////////////////////////////////////////////////////////////////////////////////
//                                                                                //
//                                   Coded by Aeren                                   //
//                                                                                //
////////////////////////////////////////////////////////////////////////////////////////