#include <bits/stdc++.h>
using namespace std;
int dx[] = {0, 0, 1, -1};
int dy[] = {-1, 1, 0, 0};
template <class T>
T power(T N, T P) {
  return (P == 0) ? 1 : N * power(N, P - 1);
}
template <class T>
T gcd(T a, T b) {
  if (b == 0) return a;
  return gcd(b, a % b);
}
struct debugger {
  template <typename T>
  debugger& operator,(const T& v) {
    cerr << v << " ";
    return *this;
  }
} dbg;
void make_unique(vector<long long int>& a) {
  sort(a.begin(), a.end());
  a.erase(unique(a.begin(), a.end()), a.end());
}
long long int bigmod(long long int B, long long int P, long long int M) {
  long long int R = 1;
  while (P > 0) {
    if (P % 2 == 1) {
      R = (R * B) % M;
    }
    P /= 2;
    B = (B * B) % M;
  }
  return R;
}
const int N = (int)2e6 + 10;
char ans[N], s[N];
int tree[4 * N], st;
void update(int node, int b, int e, int i, int j) {
  if (b > j || e < i) return;
  if (tree[node] == (e - b + 1)) return;
  if (b >= i && e <= j) {
    if (tree[node] == (e - b + 1)) return;
    for (int ii = b - st, jj = b; jj <= e; ii++, jj++) {
      ans[jj] = s[ii];
    }
    tree[node] = (e - b + 1);
    return;
  }
  int lft = node * 2, rt = lft + 1, mid = (b + e) / 2;
  update(lft, b, mid, i, j);
  update(rt, mid + 1, e, i, j);
  tree[node] = tree[lft] + tree[rt];
}
int main() {
  int n;
  while (scanf("%d", &n) != EOF) {
    memset(tree, 0, sizeof(tree));
    int mx = N - 10, mxpos = 0, len, k, pos;
    for (int i = 0; i < N - 2; i++) ans[i] = '#';
    for (int i = 1; i <= n; i++) {
      scanf("%s", &s);
      len = strlen(s);
      scanf("%d", &k);
      for (int j = 1; j <= k; j++) {
        scanf("%d", &pos);
        st = pos;
        update(1, 1, mx, pos, pos + len - 1);
        mxpos = max(mxpos, pos + len - 1);
      }
    }
    for (int i = 1; i <= mxpos; i++) {
      printf("%c", ans[i] == '#' ? 'a' : ans[i]);
    }
    printf("\n");
  }
  return 0;
}
