#include <bits/stdc++.h>
using namespace std;
template <class T>
ostream& operator<<(ostream& fout, const vector<T>& vec) {
  for (size_t i = 0; i < vec.size(); ++i) {
    fout << vec[i] << ' ';
  }
  return fout;
}
template <class T>
istream& operator>>(istream& fin, vector<T>& vec) {
  for (size_t i = 0; i < vec.size(); ++i) {
    cin >> vec[i];
  }
  return fin;
}
bool find_cycle(const vector<vector<pair<int, pair<int, bool>>>>& g, int st,
                vector<int>& used, const vector<bool>& valid,
                vector<tuple<int, int, bool>>& result, int last_edge = -1,
                bool sign = 0) {
  if (used[st] == 2) {
    return 0;
  }
  if (used[st] == 1) {
    result.emplace_back(st, last_edge, sign);
    return 1;
  }
  used[st] = 1;
  for (const auto& to_p : g[st]) {
    int to = to_p.first;
    if (valid[to] || last_edge == to_p.second.first) {
      continue;
    }
    if (find_cycle(g, to, used, valid, result, to_p.second.first,
                   to_p.second.second)) {
      result.emplace_back(st, last_edge, sign);
      return 1;
    }
  }
  used[st] = 2;
  return 0;
}
void dfs_mark(int st, vector<bool>& valid,
              const vector<vector<pair<int, pair<int, bool>>>>& g,
              vector<int>& result) {
  valid[st] = true;
  for (const auto& to_p : g[st]) {
    int to = to_p.first;
    if (valid[to] == false && result[to_p.second.first] == -1) {
      result[to_p.second.first] = to_p.second.second ^ 1;
      dfs_mark(to, valid, g, result);
    }
  }
}
int main() {
  int n, m;
  scanf("%d", &n);
  scanf("%d", &m);
  vector<int> result(m, -1);
  vector<vector<pair<int, bool>>> coords(m);
  for (int i = 0; i < n; ++i) {
    int k = 0;
    scanf("%d", &k);
    for (int j = 0; j < k; ++j) {
      pair<int, bool> coord = {i, 0};
      int index;
      scanf("%d", &index);
      if (index < 0) {
        index *= -1;
        coord.second = 1;
      }
      --index;
      coords[index].push_back(coord);
    }
  }
  vector<vector<pair<int, pair<int, bool>>>> g(n);
  vector<bool> valid(n);
  for (int i = 0; i < m; ++i) {
    if (coords[i].size() == 1) {
      valid[coords[i].front().first] = true;
      result[i] = coords[i].front().second ^ 1;
    } else if (coords[i].size() == 2) {
      if (coords[i].front().second == coords[i].back().second) {
        valid[coords[i].front().first] = true;
        valid[coords[i].back().first] = true;
        result[i] = coords[i].front().second ^ 1;
      } else {
        g[coords[i].front().first].push_back(
            {coords[i].back().first, {i, coords[i].back().second}});
        g[coords[i].back().first].push_back(
            {coords[i].front().first, {i, coords[i].front().second}});
      }
    }
  }
  for (int i = 0; i < n; ++i) {
    if (valid[i]) {
      dfs_mark(i, valid, g, result);
    }
  }
  vector<int> used(n);
  for (int i = 0; i < n; ++i) {
    if (valid[i] == false) {
      vector<tuple<int, int, bool>> cycle_i;
      bool is_cycle = find_cycle(g, i, used, valid, cycle_i);
      set<int> vertexes;
      if (is_cycle) {
        for (const auto& el : cycle_i) {
          if (vertexes.count(get<0>(el))) {
            break;
          }
          valid[get<0>(el)] = true;
          result[get<1>(el)] = get<2>(el) ^ 1;
          vertexes.insert(get<0>(el));
        }
        for (const auto& el : vertexes) {
          dfs_mark(el, valid, g, result);
        }
      }
    }
  }
  bool ok = true;
  for (const auto& el : valid) {
    ok &= el;
  }
  if (ok) {
    puts("YES");
    for (const auto& res : result) {
      if (res == -1) {
        printf("0");
      } else {
        printf("%d", res);
      }
    }
    puts("");
  } else {
    puts("NO");
  }
  return 0;
}
