Problem p02692 (C++)

Buggy submission (status: Runtime Error):
#ifndef ONLINE_JUDGE
#define _GLIBCXX_DEBUG
#endif
#include <bits/stdc++.h>

#define PI 3.14159265359
using namespace std;
#define rep(i, n) for (ll i = 0; i < (ll)(n); i++)
#define bit(n, k) (((ll)n >> (ll)k) & 1) /*nのk bit目*/
#define pb push_back
#define pf push_front
#define FI first
#define SE second
#define eb emplace_back
#define SZ(x) ((ll)(x).size())
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
const long long INF = 1e+18 + 1;
// long long INF=(1LL<<31)-1;
typedef long long ll;
typedef vector<ll> vl;
typedef vector<vector<ll>> vvl;
typedef pair<ll, ll> P;
typedef tuple<ll, ll, ll> T;
typedef struct Point_Coordinates {
  ll x, y;
} point;
const ll MOD = 1000000007LL;
// ll MOD=1000000007LL;
// const ll MOD=998244353LL;
// const ll MOD=1777777777LL;
// const ll MAX_V=114514LL;
// const ll MAX = 500010LL;
const ll mod = MOD;
string abc = "abcdefghijklmnopqrstuvwxyz";
string ABC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
vl dx = {0, 0, 1, -1};
vl dy = {1, -1, 0, 0};

// 素因数分解O(√n)
map<ll, ll> prime_factor(ll n) {
  map<ll, ll> res;
  for (ll i = 2; i * i <= n; i++) {
    while (n % i == 0) {
      res[i]++;
      n /= i;
    }
  }
  if (n != 1)
    res[n] = 1;
  return res;
}

const ll MAX = 2000010;
long long fac[MAX], finv[MAX], inv[MAX];
// finvが階乗の逆元

// テーブルを作る前処理
void COMinit() {
  fac[0] = fac[1] = 1;
  finv[0] = finv[1] = 1;
  inv[1] = 1;
  for (ll i = 2; i < MAX; i++) {
    fac[i] = fac[i - 1] * i % MOD;
    inv[i] = MOD - inv[MOD % i] * (MOD / i) % MOD;
    finv[i] = finv[i - 1] * inv[i] % MOD;
  }
}

// 二項係数計算
long long COM(ll n, ll k) {
  if (n < k)
    return 0;
  if (n < 0 || k < 0)
    return 0;
  return fac[n] * (finv[k] * finv[n - k] % MOD) % MOD;
}

ll modpow(ll a, ll n) {

  ll res = 1;
  while (n > 0) {
    if (n & 1)
      res = res * a % mod;
    a = a * a % mod;
    n >>= 1;
  }
  return res;
}

/*Eratosthenes()
ll N=2000010;
vl arr(N);
void Eratosthenes(){
        for(ll i = 0; i < N; i++){
                arr[i] = 1;
        }
        arr[1]=0;
        for(ll i = 2; i < sqrt(N); i++){
                if(arr[i]){
                        for(ll j = 0; i * (j + 2) < N; j++){
                                arr[i *(j + 2)] = 0;
                        }
                }
        }
}*/
// 素数判定O(√n)
bool is_prime(ll n) {
  for (ll i = 2; i * i <= n; i++) {
    if (n % i == 0)
      return false;
  }
  return n != 1;
}

// 約数の列挙O(√n)
vector<ll> divisor(ll n) {
  vector<ll> res;
  for (ll i = 1; i * i <= n; i++) {
    if (n % i == 0) {
      res.push_back(i);
      if (i != n / i)
        res.push_back(n / i);
    }
  }
  return res;
}

/* SegTree<X>(n,fx,ex): モノイド(集合X, 二項演算fx,
   単位元ex)についてサイズnで構築 set(int i, X x), build():
   i番目の要素をxにセット。まとめてセグ木を構築する。O(n) update(i,x): i
   番目の要素を x に更新。O(log(n)) query(a,b):  [a,b)
   全てにfxを作用させた値を取得。O(log(n))
*/
template <typename X> struct SegTree {
  using FX = function<X(X, X)>;
  int n;
  FX fx;
  const X ex;
  vector<X> dat;
  SegTree(int n_, FX fx_, X ex_) : n(), fx(fx_), ex(ex_), dat(n_ * 4, ex_) {
    int x = 1;
    while (n_ > x) {
      x *= 2;
    }
    n = x;
  }

  void set(int i, X x) { dat[i + n - 1] = x; }
  void build() {
    for (int k = n - 2; k >= 0; k--)
      dat[k] = fx(dat[2 * k + 1], dat[2 * k + 2]);
  }

  void update(int i, X x) {
    i += n - 1;
    dat[i] = x;
    while (i > 0) {
      i = (i - 1) / 2; // parent
      dat[i] = fx(dat[i * 2 + 1], dat[i * 2 + 2]);
    }
  }

  // the minimum element of [a,b)
  X query(int a, int b) { return query_sub(a, b, 0, 0, n); }
  X query_sub(int a, int b, int k, int l, int r) {
    if (r <= a || b <= l) {
      return ex;
    } else if (a <= l && r <= b) {
      return dat[k];
    } else {
      X vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2);
      X vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r);
      return fx(vl, vr);
    }
  }
  /* debug */
  inline X operator[](int a) { return query(a, a + 1); }
  void print() {
    for (int i = 0; i < 2 * n - 1; ++i) {
      cout << (*this)[i];
      if (i != n)
        cout << ",";
    }
    cout << endl;
  }
  /*
  使用例
  auto fx=[](int x1,int x2)->int{return max(x1,x2);};
  ll ex=0;
  SegTree<ll>rmq(n,fx,ex);*/
};

/* Trie 木： 文字の種類(char_size)、int型で0に対応する文字(base)
    insert(word): 単語 word を Trie 木に挿入する
    search(word): 単語 word が Trie 木にあるか判定する
    start_with(prefix):  prefix が一致する単語が Trie 木にあるか判定する
    count(): 挿入した単語の数を返す
    size(): Trie 木の頂点数を返す
    計算量：insert, search ともに O(M)（Mは単語の長さ）
*/
template <int char_size, int base> struct Trie {
  struct Node {       // 頂点を表す構造体
    vector<int> next; // 子の頂点番号を格納。存在しなければ-1
    vector<int> accept; // 末端がこの頂点になる単語の word_id を保存
    int c;              // base からの間隔をint型で表現したもの
    int common; // いくつの単語がこの頂点を共有しているか
    Node(int c_) : c(c_), common(0) { next.assign(char_size, -1); }
  };
  vector<Node> nodes; // trie 木本体
  int root;
  Trie() : root(0) { nodes.push_back(Node(root)); }
  // 単語の挿入
  void insert(const string &word, int word_id) {
    int node_id = 0;
    for (int i = 0; i < (int)word.size(); i++) {
      int c = (int)(word[i] - base);
      int &next_id = nodes[node_id].next[c];
      if (next_id == -1) { // 次の頂点が存在しなければ追加
        next_id = (int)nodes.size();
        nodes.push_back(Node(c));
      }
      ++nodes[node_id].common;
      node_id = next_id;
    }
    ++nodes[node_id].common;
    nodes[node_id].accept.push_back(word_id);
  }
  void insert(const string &word) { insert(word, nodes[0].common); }
  // 単語とprefixの検索
  bool search(const string &word, bool prefix = false) {
    int node_id = 0;
    for (int i = 0; i < (int)word.size(); i++) {
      int c = (int)(word[i] - base);
      int &next_id = nodes[node_id].next[c];
      if (next_id == -1) { // 次の頂点が存在しなければ終了
        return false;
      }
      node_id = next_id;
    }
    return (prefix) ? true : nodes[node_id].accept.size() > 0;
  }
  // prefix を持つ単語が存在するかの検索
  bool start_with(const string &prefix) { return search(prefix, true); }
  // 挿入した単語の数
  int count() const { return (nodes[0].common); }
  // Trie木のノード数
  int size() const { return ((int)nodes.size()); }
};

// union by size + path having
class UnionFind {
public:
  vector<ll> par; // 各元の親を表す配列
  vector<ll> siz; // 素集合のサイズを表す配列(1 で初期化)

  // Constructor
  UnionFind(ll sz_) : par(sz_), siz(sz_, 1LL) {
    for (ll i = 0; i < sz_; ++i)
      par[i] = i; // 初期では親は自分自身
  }
  void init(ll sz_) {
    par.resize(sz_);
    siz.assign(sz_, 1LL); // resize だとなぜか初期化されなかった
    for (ll i = 0; i < sz_; ++i)
      par[i] = i; // 初期では親は自分自身
  }

  // Member Function
  // Find
  ll root(ll x) { // 根の検索
    while (par[x] != x) {
      x = par[x] = par[par[x]]; // x の親の親を x の親とする
    }
    return x;
  }

  // Union(Unite, Merge)
  bool merge(ll x, ll y) {
    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    // merge technique（データ構造をマージするテク．小を大にくっつける）
    if (siz[x] < siz[y])
      swap(x, y);
    siz[x] += siz[y];
    par[y] = x;
    return true;
  }

  bool issame(ll x, ll y) { // 連結判定
    return root(x) == root(y);
  }

  ll size(ll x) { // 素集合のサイズ
    return siz[root(x)];
  }
};

// 0-indexed parmutation only
vvl cycle_partition(const vl &p) {
  ll n = p.size();
  vvl ret;
  vector<bool> check(n, false);
  rep(i, n) if (!check[p[i]]) {
    vl v;
    ll pos = p[i];
    v.pb(i);
    check[i] = true;
    while (pos != i) {
      v.pb(pos);
      check[pos] = true;
      pos = p[pos];
    }
    ret.pb(v);
  }
  return ret;
}

// Manachar　修理中
//  vl Manachar(string S){
//      ll c=0,n=S.size();
//      vl R(n,1);
//      for(ll i=0;i<n;i++){
//          ll l=c-(i-c);
//          if(i+R[l]<c+R[c]){
//              R[i]=R[l];
//          }else{
//              ll j=c+R[c]-i;
//              while(i-j>=0 && i+j<n && S[i-j] == S[i+j])j++;
//              R[i]=j;
//              c=i;
//          }
//      }
//      return R;
//  }

struct edge {
  ll to;
  ll cost;
};

template <typename T> T pow(T a, long long n, T e = 1) {
  T ret = e;
  while (n) {
    if (n & 1)
      ret *= a;
    a *= a;
    n >>= 1;
  }
  return ret;
}

template <int mod> struct ModInt {
  int x;
  ModInt() : x(0) {}
  ModInt(long long x_) {
    if ((x = x_ % mod + mod) >= mod)
      x -= mod;
  }
  ModInt &operator+=(ModInt rhs) {
    if ((x += rhs.x) >= mod)
      x -= mod;
    return *this;
  }
  ModInt &operator-=(ModInt rhs) {
    if ((x -= rhs.x) < 0)
      x += mod;
    return *this;
  }
  ModInt &operator*=(ModInt rhs) {
    x = (unsigned long long)x * rhs.x % mod;
    return *this;
  }
  ModInt &operator/=(ModInt rhs) {
    x = (unsigned long long)x * rhs.inv().x % mod;
    return *this;
  }

  ModInt operator-() const { return -x < 0 ? mod - x : -x; }
  ModInt operator+(ModInt rhs) const { return ModInt(*this) += rhs; }
  ModInt operator-(ModInt rhs) const { return ModInt(*this) -= rhs; }
  ModInt operator*(ModInt rhs) const { return ModInt(*this) *= rhs; }
  ModInt operator/(ModInt rhs) const { return ModInt(*this) /= rhs; }
  bool operator==(ModInt rhs) const { return x == rhs.x; }
  bool operator!=(ModInt rhs) const { return x != rhs.x; }
  ModInt inv() const { return pow(*this, mod - 2); }

  friend ostream &operator<<(ostream &s, ModInt<mod> a) {
    s << a.x;
    return s;
  }
  friend istream &operator>>(istream &s, ModInt<mod> &a) {
    s >> a.x;
    return s;
  }
};

using mint = ModInt<MOD>;

/* RMQ：[0,n-1] について、区間ごとの最小値を管理する構造体
    update(a,b,x): 区間[a,b) の要素を x に更新。O(log(n))
    query(a,b): [a,b) での最小の要素を取得。O(log(n))
*/
template <typename T> struct RMQ {
  // const T INF = numeric_limits<T>::max();
  int n;
  vector<T> dat, lazy;
  RMQ(int n_) : n(), dat(n_ * 4, INF), lazy(n_ * 4, INF) {
    int x = 1;
    while (n_ > x)
      x *= 2;
    n = x;
  }
  /* lazy eval */
  void eval(int k) {
    if (lazy[k] == INF)
      return;        // 更新するものが無ければ終了
    if (k < n - 1) { // 葉でなければ子に伝搬
      lazy[k * 2 + 1] = lazy[k];
      lazy[k * 2 + 2] = lazy[k];
    }
    // 自身を更新
    dat[k] = lazy[k];
    lazy[k] = INF;
  }
  void update(int a, int b, T x, int k, int l, int r) {
    eval(k);
    if (a <= l && r <= b) { // 完全に内側の時
      lazy[k] = x;
      eval(k);
    } else if (a < r && l < b) {                  // 一部区間が被る時
      update(a, b, x, k * 2 + 1, l, (l + r) / 2); // 左の子
      update(a, b, x, k * 2 + 2, (l + r) / 2, r); // 右の子
      dat[k] = min(dat[k * 2 + 1], dat[k * 2 + 2]);
    }
  }
  void update(int a, int b, T x) { update(a, b, x, 0, 0, n); }
  T query_sub(int a, int b, int k, int l, int r) {
    eval(k);
    if (r <= a || b <= l) { // 完全に外側の時
      return INF;
    } else if (a <= l && r <= b) { // 完全に内側の時
      return dat[k];
    } else { // 一部区間が被る時
      T vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2);
      T vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r);
      return min(vl, vr);
    }
  }
  T query(int a, int b) { return query_sub(a, b, 0, 0, n); }
  /* debug */
  inline T operator[](int a) { return query(a, a + 1); }
  void print() {
    for (int i = 0; i < 2 * n - 1; ++i) {
      cout << (*this)[i];
      if (i != n)
        cout << ",";
    }
    cout << endl;
  }
};

int main() {
  ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  cout << fixed << setprecision(10);
  /*--------------------------------*/

  ll n, a, b, c;
  cin >> n >> a >> b >> c;
  vector<string> s(n);
  rep(i, n) cin >> s[i];
  if (a == 0 and b == 0 and c == 0) {
    cout << "No" << endl;
    return 0;
  }
  vector<char> ans;
  if (a + b + c == 1) {
    rep(i, n) {
      if (s[i] == "AB") {
        if (a == 0 and b == 0) {
          cout << "No" << endl;
          return 0;
        }
        if (a == 1) {
          a--;
          b++;
          ans.pb('B');
        } else {
          b--;
          a++;
          ans.pb('A');
        }
      }
      if (s[i] == "AC") {
        if (a == 0 and c == 0) {
          cout << "No" << endl;
          return 0;
        }
        if (a == 1) {
          a--;
          c++;
          ans.pb('C');
        } else {
          c--;
          a++;
          ans.pb('A');
        }
      }
      if (s[i] == "BC") {
        if (b == 0 and c == 0) {
          cout << "No" << endl;
          return 0;
        }
        if (b == 1) {
          b--;
          c++;
          ans.pb('C');
        } else {
          c--;
          b++;
          ans.pb('B');
        }
      }
    }
    cout << "Yes" << endl;
    rep(i, n) cout << ans[i] << endl;
    return 0;
  }
  if (s[0] == "AB") {
    if (a == 0 and b == 0) {
      cout << "No" << endl;
      return 0;
    }
  }
  if (s[0] == "AC") {
    if (a == 0 and c == 0) {
      cout << "No" << endl;
      return 0;
    }
  }
  if (s[0] == "BC") {
    if (b == 0 and c == 0) {
      cout << "No" << endl;
      return 0;
    }
  }
  rep(i, n - 1) {
    if (s[i] == "AB") {
      if (a > 0 and b == 0) {
        a--;
        b++;
        ans.pb('B');
      } else if (a == 0 and b > 0) {
        a++;
        b--;
        ans.pb('A');
      }
    } else if (s[i] == "AC") {
      if (a > 0 and c == 0) {
        a--;
        c++;
        ans.pb('C');
      } else if (a == 0 and c > 0) {
        a++;
        c--;
        ans.pb('A');
      }
    } else if (s[i] == "BC") {
      if (b > 0 and c == 0) {
        b--;
        c++;
        ans.pb('C');
      } else if (b == 0 and c > 0) {
        b++;
        c--;
        ans.pb('B');
      }
    } else if (s[i] == "AB" and s[i + 1] == "AB") {
      if (a >= b) {
        b++;
        a--;
        ans.pb('B');
      } else {
        b--;
        a++;
        ans.pb('A');
      }
    } else if (s[i] == "AC" and s[i + 1] == "AC") {
      if (a >= c) {
        c++;
        a--;
        ans.pb('C');
      } else {
        c--;
        a++;
        ans.pb('A');
      }
    } else if (s[i] == "BC" and s[i + 1] == "BC") {
      if (c >= b) {
        b++;
        c--;
        ans.pb('B');
      } else {
        b--;
        c++;
        ans.pb('C');
      }
    }

    else if (s[i] == "AC" and s[i + 1] == "AB") {
      a++;
      c--;
      ans.pb('A');
    } else if (s[i] == "AC" and s[i + 1] == "BC") {
      c++;
      a--;
      ans.pb('C');
    } else if (s[i] == "AB" and s[i + 1] == "AC") {
      a++;
      b--;
      ans.pb('A');
    } else if (s[i] == "AB" and s[i + 1] == "BC") {
      b++;
      a--;
      ans.pb('B');
    } else if (s[i] == "BC" and s[i + 1] == "AB") {
      b++;
      c--;
      ans.pb('B');
    } else {
      c++;
      b--;
      ans.pb('C');
    }
    if (a < 0 || b < 0 || c < 0) {
      cout << "No" << endl;
      return 0;
    }
  }

  if (s[n - 1] == "AB") {
    if (a > 0)
      ans.pb('B');
    else if (b > 0)
      ans.pb('A');
    else {
      cout << "No" << endl;
      return 0;
    }
  }
  if (s[n - 1] == "BC") {
    if (b > 0)
      ans.pb('C');
    else if (c > 0)
      ans.pb('B');
    else {
      cout << "No" << endl;
      return 0;
    }
  }
  if (s[n - 1] == "AC") {
    if (a > 0)
      ans.pb('C');
    else if (c > 0)
      ans.pb('A');
    else {
      cout << "No" << endl;
      return 0;
    }
  }
  cout << "Yes" << endl;
  rep(i, n) cout << ans[i] << endl;
}


Fixed submission:
#ifndef ONLINE_JUDGE
#define _GLIBCXX_DEBUG
#endif
#include <bits/stdc++.h>

#define PI 3.14159265359
using namespace std;
#define rep(i, n) for (ll i = 0; i < (ll)(n); i++)
#define bit(n, k) (((ll)n >> (ll)k) & 1) /*nのk bit目*/
#define pb push_back
#define pf push_front
#define FI first
#define SE second
#define eb emplace_back
#define SZ(x) ((ll)(x).size())
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
const long long INF = 1e+18 + 1;
// long long INF=(1LL<<31)-1;
typedef long long ll;
typedef vector<ll> vl;
typedef vector<vector<ll>> vvl;
typedef pair<ll, ll> P;
typedef tuple<ll, ll, ll> T;
typedef struct Point_Coordinates {
  ll x, y;
} point;
const ll MOD = 1000000007LL;
// ll MOD=1000000007LL;
// const ll MOD=998244353LL;
// const ll MOD=1777777777LL;
// const ll MAX_V=114514LL;
// const ll MAX = 500010LL;
const ll mod = MOD;
string abc = "abcdefghijklmnopqrstuvwxyz";
string ABC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
vl dx = {0, 0, 1, -1};
vl dy = {1, -1, 0, 0};

// 素因数分解O(√n)
map<ll, ll> prime_factor(ll n) {
  map<ll, ll> res;
  for (ll i = 2; i * i <= n; i++) {
    while (n % i == 0) {
      res[i]++;
      n /= i;
    }
  }
  if (n != 1)
    res[n] = 1;
  return res;
}

const ll MAX = 2000010;
long long fac[MAX], finv[MAX], inv[MAX];
// finvが階乗の逆元

// テーブルを作る前処理
void COMinit() {
  fac[0] = fac[1] = 1;
  finv[0] = finv[1] = 1;
  inv[1] = 1;
  for (ll i = 2; i < MAX; i++) {
    fac[i] = fac[i - 1] * i % MOD;
    inv[i] = MOD - inv[MOD % i] * (MOD / i) % MOD;
    finv[i] = finv[i - 1] * inv[i] % MOD;
  }
}

// 二項係数計算
long long COM(ll n, ll k) {
  if (n < k)
    return 0;
  if (n < 0 || k < 0)
    return 0;
  return fac[n] * (finv[k] * finv[n - k] % MOD) % MOD;
}

ll modpow(ll a, ll n) {

  ll res = 1;
  while (n > 0) {
    if (n & 1)
      res = res * a % mod;
    a = a * a % mod;
    n >>= 1;
  }
  return res;
}

/*Eratosthenes()
ll N=2000010;
vl arr(N);
void Eratosthenes(){
        for(ll i = 0; i < N; i++){
                arr[i] = 1;
        }
        arr[1]=0;
        for(ll i = 2; i < sqrt(N); i++){
                if(arr[i]){
                        for(ll j = 0; i * (j + 2) < N; j++){
                                arr[i *(j + 2)] = 0;
                        }
                }
        }
}*/
// 素数判定O(√n)
bool is_prime(ll n) {
  for (ll i = 2; i * i <= n; i++) {
    if (n % i == 0)
      return false;
  }
  return n != 1;
}

// 約数の列挙O(√n)
vector<ll> divisor(ll n) {
  vector<ll> res;
  for (ll i = 1; i * i <= n; i++) {
    if (n % i == 0) {
      res.push_back(i);
      if (i != n / i)
        res.push_back(n / i);
    }
  }
  return res;
}

/* SegTree<X>(n,fx,ex): モノイド(集合X, 二項演算fx,
   単位元ex)についてサイズnで構築 set(int i, X x), build():
   i番目の要素をxにセット。まとめてセグ木を構築する。O(n) update(i,x): i
   番目の要素を x に更新。O(log(n)) query(a,b):  [a,b)
   全てにfxを作用させた値を取得。O(log(n))
*/
template <typename X> struct SegTree {
  using FX = function<X(X, X)>;
  int n;
  FX fx;
  const X ex;
  vector<X> dat;
  SegTree(int n_, FX fx_, X ex_) : n(), fx(fx_), ex(ex_), dat(n_ * 4, ex_) {
    int x = 1;
    while (n_ > x) {
      x *= 2;
    }
    n = x;
  }

  void set(int i, X x) { dat[i + n - 1] = x; }
  void build() {
    for (int k = n - 2; k >= 0; k--)
      dat[k] = fx(dat[2 * k + 1], dat[2 * k + 2]);
  }

  void update(int i, X x) {
    i += n - 1;
    dat[i] = x;
    while (i > 0) {
      i = (i - 1) / 2; // parent
      dat[i] = fx(dat[i * 2 + 1], dat[i * 2 + 2]);
    }
  }

  // the minimum element of [a,b)
  X query(int a, int b) { return query_sub(a, b, 0, 0, n); }
  X query_sub(int a, int b, int k, int l, int r) {
    if (r <= a || b <= l) {
      return ex;
    } else if (a <= l && r <= b) {
      return dat[k];
    } else {
      X vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2);
      X vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r);
      return fx(vl, vr);
    }
  }
  /* debug */
  inline X operator[](int a) { return query(a, a + 1); }
  void print() {
    for (int i = 0; i < 2 * n - 1; ++i) {
      cout << (*this)[i];
      if (i != n)
        cout << ",";
    }
    cout << endl;
  }
  /*
  使用例
  auto fx=[](int x1,int x2)->int{return max(x1,x2);};
  ll ex=0;
  SegTree<ll>rmq(n,fx,ex);*/
};

/* Trie 木： 文字の種類(char_size)、int型で0に対応する文字(base)
    insert(word): 単語 word を Trie 木に挿入する
    search(word): 単語 word が Trie 木にあるか判定する
    start_with(prefix):  prefix が一致する単語が Trie 木にあるか判定する
    count(): 挿入した単語の数を返す
    size(): Trie 木の頂点数を返す
    計算量：insert, search ともに O(M)（Mは単語の長さ）
*/
template <int char_size, int base> struct Trie {
  struct Node {       // 頂点を表す構造体
    vector<int> next; // 子の頂点番号を格納。存在しなければ-1
    vector<int> accept; // 末端がこの頂点になる単語の word_id を保存
    int c;              // base からの間隔をint型で表現したもの
    int common; // いくつの単語がこの頂点を共有しているか
    Node(int c_) : c(c_), common(0) { next.assign(char_size, -1); }
  };
  vector<Node> nodes; // trie 木本体
  int root;
  Trie() : root(0) { nodes.push_back(Node(root)); }
  // 単語の挿入
  void insert(const string &word, int word_id) {
    int node_id = 0;
    for (int i = 0; i < (int)word.size(); i++) {
      int c = (int)(word[i] - base);
      int &next_id = nodes[node_id].next[c];
      if (next_id == -1) { // 次の頂点が存在しなければ追加
        next_id = (int)nodes.size();
        nodes.push_back(Node(c));
      }
      ++nodes[node_id].common;
      node_id = next_id;
    }
    ++nodes[node_id].common;
    nodes[node_id].accept.push_back(word_id);
  }
  void insert(const string &word) { insert(word, nodes[0].common); }
  // 単語とprefixの検索
  bool search(const string &word, bool prefix = false) {
    int node_id = 0;
    for (int i = 0; i < (int)word.size(); i++) {
      int c = (int)(word[i] - base);
      int &next_id = nodes[node_id].next[c];
      if (next_id == -1) { // 次の頂点が存在しなければ終了
        return false;
      }
      node_id = next_id;
    }
    return (prefix) ? true : nodes[node_id].accept.size() > 0;
  }
  // prefix を持つ単語が存在するかの検索
  bool start_with(const string &prefix) { return search(prefix, true); }
  // 挿入した単語の数
  int count() const { return (nodes[0].common); }
  // Trie木のノード数
  int size() const { return ((int)nodes.size()); }
};

// union by size + path having
class UnionFind {
public:
  vector<ll> par; // 各元の親を表す配列
  vector<ll> siz; // 素集合のサイズを表す配列(1 で初期化)

  // Constructor
  UnionFind(ll sz_) : par(sz_), siz(sz_, 1LL) {
    for (ll i = 0; i < sz_; ++i)
      par[i] = i; // 初期では親は自分自身
  }
  void init(ll sz_) {
    par.resize(sz_);
    siz.assign(sz_, 1LL); // resize だとなぜか初期化されなかった
    for (ll i = 0; i < sz_; ++i)
      par[i] = i; // 初期では親は自分自身
  }

  // Member Function
  // Find
  ll root(ll x) { // 根の検索
    while (par[x] != x) {
      x = par[x] = par[par[x]]; // x の親の親を x の親とする
    }
    return x;
  }

  // Union(Unite, Merge)
  bool merge(ll x, ll y) {
    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    // merge technique（データ構造をマージするテク．小を大にくっつける）
    if (siz[x] < siz[y])
      swap(x, y);
    siz[x] += siz[y];
    par[y] = x;
    return true;
  }

  bool issame(ll x, ll y) { // 連結判定
    return root(x) == root(y);
  }

  ll size(ll x) { // 素集合のサイズ
    return siz[root(x)];
  }
};

// 0-indexed parmutation only
vvl cycle_partition(const vl &p) {
  ll n = p.size();
  vvl ret;
  vector<bool> check(n, false);
  rep(i, n) if (!check[p[i]]) {
    vl v;
    ll pos = p[i];
    v.pb(i);
    check[i] = true;
    while (pos != i) {
      v.pb(pos);
      check[pos] = true;
      pos = p[pos];
    }
    ret.pb(v);
  }
  return ret;
}

// Manachar　修理中
//  vl Manachar(string S){
//      ll c=0,n=S.size();
//      vl R(n,1);
//      for(ll i=0;i<n;i++){
//          ll l=c-(i-c);
//          if(i+R[l]<c+R[c]){
//              R[i]=R[l];
//          }else{
//              ll j=c+R[c]-i;
//              while(i-j>=0 && i+j<n && S[i-j] == S[i+j])j++;
//              R[i]=j;
//              c=i;
//          }
//      }
//      return R;
//  }

struct edge {
  ll to;
  ll cost;
};

template <typename T> T pow(T a, long long n, T e = 1) {
  T ret = e;
  while (n) {
    if (n & 1)
      ret *= a;
    a *= a;
    n >>= 1;
  }
  return ret;
}

template <int mod> struct ModInt {
  int x;
  ModInt() : x(0) {}
  ModInt(long long x_) {
    if ((x = x_ % mod + mod) >= mod)
      x -= mod;
  }
  ModInt &operator+=(ModInt rhs) {
    if ((x += rhs.x) >= mod)
      x -= mod;
    return *this;
  }
  ModInt &operator-=(ModInt rhs) {
    if ((x -= rhs.x) < 0)
      x += mod;
    return *this;
  }
  ModInt &operator*=(ModInt rhs) {
    x = (unsigned long long)x * rhs.x % mod;
    return *this;
  }
  ModInt &operator/=(ModInt rhs) {
    x = (unsigned long long)x * rhs.inv().x % mod;
    return *this;
  }

  ModInt operator-() const { return -x < 0 ? mod - x : -x; }
  ModInt operator+(ModInt rhs) const { return ModInt(*this) += rhs; }
  ModInt operator-(ModInt rhs) const { return ModInt(*this) -= rhs; }
  ModInt operator*(ModInt rhs) const { return ModInt(*this) *= rhs; }
  ModInt operator/(ModInt rhs) const { return ModInt(*this) /= rhs; }
  bool operator==(ModInt rhs) const { return x == rhs.x; }
  bool operator!=(ModInt rhs) const { return x != rhs.x; }
  ModInt inv() const { return pow(*this, mod - 2); }

  friend ostream &operator<<(ostream &s, ModInt<mod> a) {
    s << a.x;
    return s;
  }
  friend istream &operator>>(istream &s, ModInt<mod> &a) {
    s >> a.x;
    return s;
  }
};

using mint = ModInt<MOD>;

/* RMQ：[0,n-1] について、区間ごとの最小値を管理する構造体
    update(a,b,x): 区間[a,b) の要素を x に更新。O(log(n))
    query(a,b): [a,b) での最小の要素を取得。O(log(n))
*/
template <typename T> struct RMQ {
  // const T INF = numeric_limits<T>::max();
  int n;
  vector<T> dat, lazy;
  RMQ(int n_) : n(), dat(n_ * 4, INF), lazy(n_ * 4, INF) {
    int x = 1;
    while (n_ > x)
      x *= 2;
    n = x;
  }
  /* lazy eval */
  void eval(int k) {
    if (lazy[k] == INF)
      return;        // 更新するものが無ければ終了
    if (k < n - 1) { // 葉でなければ子に伝搬
      lazy[k * 2 + 1] = lazy[k];
      lazy[k * 2 + 2] = lazy[k];
    }
    // 自身を更新
    dat[k] = lazy[k];
    lazy[k] = INF;
  }
  void update(int a, int b, T x, int k, int l, int r) {
    eval(k);
    if (a <= l && r <= b) { // 完全に内側の時
      lazy[k] = x;
      eval(k);
    } else if (a < r && l < b) {                  // 一部区間が被る時
      update(a, b, x, k * 2 + 1, l, (l + r) / 2); // 左の子
      update(a, b, x, k * 2 + 2, (l + r) / 2, r); // 右の子
      dat[k] = min(dat[k * 2 + 1], dat[k * 2 + 2]);
    }
  }
  void update(int a, int b, T x) { update(a, b, x, 0, 0, n); }
  T query_sub(int a, int b, int k, int l, int r) {
    eval(k);
    if (r <= a || b <= l) { // 完全に外側の時
      return INF;
    } else if (a <= l && r <= b) { // 完全に内側の時
      return dat[k];
    } else { // 一部区間が被る時
      T vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2);
      T vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r);
      return min(vl, vr);
    }
  }
  T query(int a, int b) { return query_sub(a, b, 0, 0, n); }
  /* debug */
  inline T operator[](int a) { return query(a, a + 1); }
  void print() {
    for (int i = 0; i < 2 * n - 1; ++i) {
      cout << (*this)[i];
      if (i != n)
        cout << ",";
    }
    cout << endl;
  }
};

int main() {
  ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  cout << fixed << setprecision(10);
  /*--------------------------------*/

  ll n, a, b, c;
  cin >> n >> a >> b >> c;
  vector<string> s(n);
  rep(i, n) cin >> s[i];
  if (a == 0 and b == 0 and c == 0) {
    cout << "No" << endl;
    return 0;
  }
  vector<char> ans;
  if (a + b + c == 1) {
    rep(i, n) {
      if (s[i] == "AB") {
        if (a == 0 and b == 0) {
          cout << "No" << endl;
          return 0;
        }
        if (a == 1) {
          a--;
          b++;
          ans.pb('B');
        } else {
          b--;
          a++;
          ans.pb('A');
        }
      }
      if (s[i] == "AC") {
        if (a == 0 and c == 0) {
          cout << "No" << endl;
          return 0;
        }
        if (a == 1) {
          a--;
          c++;
          ans.pb('C');
        } else {
          c--;
          a++;
          ans.pb('A');
        }
      }
      if (s[i] == "BC") {
        if (b == 0 and c == 0) {
          cout << "No" << endl;
          return 0;
        }
        if (b == 1) {
          b--;
          c++;
          ans.pb('C');
        } else {
          c--;
          b++;
          ans.pb('B');
        }
      }
    }
    cout << "Yes" << endl;
    rep(i, n) cout << ans[i] << endl;
    return 0;
  }
  if (s[0] == "AB") {
    if (a == 0 and b == 0) {
      cout << "No" << endl;
      return 0;
    }
  }
  if (s[0] == "AC") {
    if (a == 0 and c == 0) {
      cout << "No" << endl;
      return 0;
    }
  }
  if (s[0] == "BC") {
    if (b == 0 and c == 0) {
      cout << "No" << endl;
      return 0;
    }
  }
  rep(i, n - 1) {
    if (s[i] == "AB" and a > 0 and b == 0) {

      a--;
      b++;
      ans.pb('B');
    } else if (s[i] == "AB" and a == 0 and b > 0) {
      a++;
      b--;
      ans.pb('A');

    } else if (s[i] == "AC" and (a > 0 and c == 0)) {
      a--;
      c++;
      ans.pb('C');
    } else if (s[i] == "AC" and a == 0 and c > 0) {
      a++;
      c--;
      ans.pb('A');
    } else if (s[i] == "BC" and (b > 0 and c == 0)) {
      b--;
      c++;
      ans.pb('C');
    } else if (s[i] == "BC" and b == 0 and c > 0) {
      b++;
      c--;
      ans.pb('B');
    }

    else if (s[i] == "AB" and s[i + 1] == "AB") {
      if (a >= b) {
        b++;
        a--;
        ans.pb('B');
      } else {
        b--;
        a++;
        ans.pb('A');
      }
    } else if (s[i] == "AC" and s[i + 1] == "AC") {
      if (a >= c) {
        c++;
        a--;
        ans.pb('C');
      } else {
        c--;
        a++;
        ans.pb('A');
      }
    } else if (s[i] == "BC" and s[i + 1] == "BC") {
      if (c >= b) {
        b++;
        c--;
        ans.pb('B');
      } else {
        b--;
        c++;
        ans.pb('C');
      }
    }

    else if (s[i] == "AC" and s[i + 1] == "AB") {
      a++;
      c--;
      ans.pb('A');
    } else if (s[i] == "AC" and s[i + 1] == "BC") {
      c++;
      a--;
      ans.pb('C');
    } else if (s[i] == "AB" and s[i + 1] == "AC") {
      a++;
      b--;
      ans.pb('A');
    } else if (s[i] == "AB" and s[i + 1] == "BC") {
      b++;
      a--;
      ans.pb('B');
    } else if (s[i] == "BC" and s[i + 1] == "AB") {
      b++;
      c--;
      ans.pb('B');
    } else {
      c++;
      b--;
      ans.pb('C');
    }
    if (a < 0 || b < 0 || c < 0) {
      cout << "No" << endl;
      return 0;
    }
  }

  if (s[n - 1] == "AB") {
    if (a > 0)
      ans.pb('B');
    else if (b > 0)
      ans.pb('A');
    else {
      cout << "No" << endl;
      return 0;
    }
  }
  if (s[n - 1] == "BC") {
    if (b > 0)
      ans.pb('C');
    else if (c > 0)
      ans.pb('B');
    else {
      cout << "No" << endl;
      return 0;
    }
  }
  if (s[n - 1] == "AC") {
    if (a > 0)
      ans.pb('C');
    else if (c > 0)
      ans.pb('A');
    else {
      cout << "No" << endl;
      return 0;
    }
  }
  cout << "Yes" << endl;
  rep(i, n) cout << ans[i] << endl;
}