#include <bits/stdc++.h>
using namespace std;
const long double eps = 1e-9;
const long double pi = acos(-1.0);
const long long inf = 1e+9;
#pragma comment(linker, "/STACK:36777216")
template <typename T>
istream &operator>>(istream &, vector<T> &);
template <typename T>
ostream &operator<<(ostream &, const vector<T> &);
vector<int> ans;
vector<int> used;
vector<vector<int> > a;
vector<vector<int> > sp;
vector<vector<int> > pos;
void dfs(int p) {
  used[p] = 1;
  for (int i = 0; i < a[p].size(); i++) {
    int u = a[p][i];
    int U = abs(u);
    if (ans[U] == -1) {
      ans[U] = u > 0 ? 0 : 1;
      if (!used[pos[U][0]]) dfs(pos[U][0]);
      if (pos[U].size() > 1 && !used[pos[U][1]]) dfs(pos[U][1]);
    }
  }
}
struct edge {
  int u, v, var;
  int su;
  edge() {}
  edge(int u, int v, int var, int su) : u(u), v(v), var(var), su(su) {}
};
vector<vector<int> > g;
vector<edge> ed;
vector<int> cic;
vector<int> used2;
vector<int> par;
edge goodEdge;
void dfs2(int v, int p = -1) {
  used2[v] = 1;
  for (int i = 0; i < g[v].size(); i++) {
    int id = g[v][i];
    int to = ed[id].u == v ? ed[id].v : ed[id].u;
    if (id == p) continue;
    if (cic.size()) return;
    if (used2[to] == 1) {
      int j = v;
      while (j != to) {
        cic.push_back(j);
        j = par[j];
      }
      cic.push_back(to);
      goodEdge = ed[id];
      return;
    } else if (used2[to] == 0) {
      par[to] = v;
      dfs2(to, id);
    }
  }
  used2[v] = 2;
}
void solve() {
  int n, m;
  scanf("%d %d", &n, &m);
  sp.resize(m + 1);
  pos.resize(m + 1);
  a.resize(n);
  ans.resize(m + 1, -1);
  vector<int> allin(m + 1);
  for (int i = 0; i < n; i++) {
    int k;
    scanf("%d", &k);
    vector<int> tmp(k);
    for (int j = 0; j < k; j++) {
      int v;
      scanf("%d", &v);
      tmp[j] = v;
      allin[abs(v)] = 1;
      sp[abs(v)].push_back(v > 0);
      pos[abs(v)].push_back(i);
    }
    a[i] = tmp;
  }
  for (int i = 1; i <= m; i++)
    if (allin[i] == 0) ans[i] = 1;
  for (int i = 1; i <= m; i++)
    if (ans[i] == -1 &&
        (sp[i].size() == 2 && sp[i][0] ^ sp[i][1] == 0 || sp[i].size() == 1))
      ans[i] = sp[i][0] ? 1 : 0;
  used.assign(n, 0);
  for (int i = 1; i <= m; i++) {
    if (ans[i] != -1 && allin[i] == 1) {
      if (!used[pos[i][0]]) dfs(pos[i][0]);
      if (pos[i].size() > 1 && !used[pos[i][1]]) dfs(pos[i][1]);
    }
  }
  g.resize(n);
  for (int i = 1; i <= m; i++) {
    if (ans[i] == -1) {
      int p1 = pos[i][0];
      int p2 = pos[i][1];
      g[p1].push_back(ed.size());
      g[p2].push_back(ed.size());
      ed.push_back(edge(p1, p2, i, sp[i][0]));
    }
  }
  for (int i = 1; i <= m; i++) {
    if (ans[i] == -1 && used[pos[i][0]] == 0) {
      used2.assign(n, 0);
      par.assign(n, -1);
      cic.clear();
      dfs2(pos[i][0]);
      if (cic.size() == 0) {
        cout << "NO" << endl;
        return;
      }
      int var = goodEdge.var;
      ans[var] = sp[var][0] ? 1 : 0;
      dfs(pos[var][0]);
    }
  }
  for (int i = 1; i <= m; i++)
    if (ans[i] == -1) ans[i] = 0;
  cout << "YES" << endl;
  cout << ans << endl;
}
int main() {
  solve();
  return 0;
}
template <typename T>
istream &operator>>(istream &is, vector<T> &v) {
  for (int i = 0; i < v.size(); ++i) is >> v[i];
  return is;
}
template <typename T>
ostream &operator<<(ostream &os, const vector<T> &v) {
  for (int i = 1; i < v.size(); i++) os << v[i];
  return os;
}
