#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
namespace AhoCorasick {
using T = int;
constexpr int B = 'a', N = 14;
struct Node {
  T val = T(), ans = -1;
  int idx;
  Node *link, *ch[N], *nxt[15], *query[N];
  ll f[15];
};
void insert(Node *o, const string &s, int val) {
  for (auto c : s) {
    if (!o->ch[c - B]) o->ch[c - B] = new Node();
    o = o->ch[c - B];
  }
  o->val += val;
}
void build_fail(Node *root, vector<Node *> &nodes) {
  queue<Node *> qu;
  qu.push(root);
  while (!qu.empty()) {
    Node *t = qu.front();
    t->idx = nodes.size();
    nodes.push_back(t);
    qu.pop();
    for (int d = 0; d < N; d++) {
      if (t->ch[d]) {
        Node *nxt = t->ch[d], *u = t->link;
        while (u && !u->ch[d]) u = u->link;
        nxt->link = u ? u->ch[d] : root, qu.push(nxt);
      }
    }
  }
}
void pre_calc(Node *root) {
  queue<Node *> qu;
  qu.push(root);
  while (!qu.empty()) {
    Node *u = qu.front();
    qu.pop();
    u->ans = u->val + (u->link ? u->link->ans : 0);
    for (int d = 0; d < N; ++d) {
      if (u->ch[d]) {
        qu.push(u->ch[d]);
        u->query[d] = u->ch[d];
      } else {
        auto t = u;
        while (t && !t->ch[d]) t = t->link;
        u->query[d] = !t ? root : t->ch[d];
      }
    }
  }
}
};  // namespace AhoCorasick
using AhoCorasick::Node;
int k;
string s;
Node *root = new Node();
vector<Node *> nodes;
vector<int> poses;
ll dp[2][1 << 14][1005];
int main() {
  ios::sync_with_stdio(false);
  cin >> k;
  for (int i = 0; i < k; ++i) {
    string s;
    int val;
    cin >> s >> val;
    AhoCorasick::insert(root, s, val);
  }
  AhoCorasick::build_fail(root, nodes);
  AhoCorasick::pre_calc(root);
  cin >> s;
  poses.push_back(-1);
  for (int i = 0; i < s.size(); ++i) {
    if (s[i] == '?') poses.push_back(i);
  }
  for (int i = 0; i < nodes.size(); ++i) {
    for (int j = 0; j < poses.size(); ++j) {
      auto t = nodes[i];
      nodes[i]->f[j] = 0;
      for (int l = poses[j] + 1;
           l < (j + 1 == poses.size() ? s.size() : poses[j + 1]); ++l) {
        t = t->query[s[l] - 'a'];
        nodes[i]->f[j] += t->ans;
      }
      nodes[i]->nxt[j] = t;
    }
  }
  for (int mask = 0; mask < (1 << 14); ++mask) {
    for (int j = 0; j < nodes.size(); ++j) {
      dp[poses.size() & 1][mask][j] = 0;
    }
  }
  for (int i = poses.size() - 1; i > 0; --i) {
    for (int mask = 0; mask < (1 << 14); ++mask) {
      if (__builtin_popcount(mask) + 1 != i) continue;
      auto &a = dp[i & 1][mask];
      fill(a, a + nodes.size(), numeric_limits<ll>::min());
      for (int idx = 0; idx < 14; ++idx) {
        if (mask & 1 << idx) continue;
        auto &b = dp[~i & 1][mask | 1 << idx];
        for (int j = 0; j < nodes.size(); ++j) {
          auto u = nodes[j]->query[idx];
          ll ans = u->ans + u->f[i];
          a[j] = max(a[j], ans + b[u->nxt[i]->idx]);
        }
      }
    }
  }
  ll ans = root->f[0] + dp[1][0][root->nxt[0]->idx];
  cout << ans << endl;
}
