// O(6^k * (n + m)) // Compile flags -Wall -Wextra -Wshadow -D_GLIBCXX_ASSERTIONS -DDEBUG -ggdb3 -fmax-errors=2 #include using namespace std; #define PB push_back #define F first #define S second #define MP make_pair #define MTP make_tuple typedef long long int ll; ll ABS(ll n) {return n >= 0 ? n : -n;} constexpr int kN = int(4E3 + 10); // constexpr int kMod = 998244353; // constexpr int kInf = 0x3f3f3f3f // constexpr ll kInf = 0x3f3f3f3f3f3f3f3f; int a[kN], b[kN], c[kN], p[kN], ans[kN], dep[kN], low[kN], scc[kN]; bitset is_big, in, went; bitset<26> bs[kN]; vector big_edge[kN], small_edge[kN], colors[kN], new_colors[kN], graph[kN]; vector big, small; stack st; int Find(int n) {return n == p[n] ? n : p[n] = Find(p[n]);} void Merge(int l, int r) { l = Find(l), r = Find(r); p[r] = l; bs[l] &= bs[r]; return ; } void tarjan(int cur) { static int t = 0, scccnt = 0; st.push(cur); low[cur] = dep[cur] = ++t; in[cur] = went[cur] = true; for (int i : graph[cur]) { if (!went[i]) tarjan(i); if (in[i]) low[cur] = min(low[cur], low[i]); } if (low[cur] == dep[cur]) { while (st.top() != cur) { in[st.top()] = false; scc[st.top()] = scccnt; st.pop(); } // st.top() == cur in[st.top()] = false; scc[st.top()] = scccnt++; st.pop(); } return ; } bool check() { // make all small degree 2 queue q; for (int i : big) for (int j : big_edge[i]) if (ans[j] == ans[i]) return false; for (int i : small) new_colors[i].clear(); for (int i : small) ans[i] = -1; for (int i : small) { for (int color : colors[i]) { for (int j : big_edge[i]) if (ans[j] == color) goto check_Bad; check_Good: new_colors[i].PB(color); check_Bad: continue; } if (int(new_colors[i].size()) == 0) return false; else if (int(new_colors[i].size()) == 1) q.push(i); } while (!q.empty()) { int cur = q.front(); q.pop(); ans[cur] = new_colors[cur][0]; for (int i : small_edge[cur]) { vector tmp; swap(tmp, new_colors[i]); for (int color : tmp) if (color != ans[cur]) new_colors[i].PB(color); if (int(tmp.size()) > int(new_colors[i].size())) { if (new_colors[i].empty()) return false; else q.push(i); } } } // int(new_colors[i].size()) == 2 for (int i : small) if (ans[i] == -1) { graph[i << 1].clear(); graph[i << 1 | 1].clear(); } for (int i : small) if (ans[i] == -1) { for (int j : small_edge[i]) if (ans[j] == -1) { for (int x = 0; x < 2; x++) for (int y = 0; y < 2; y++) if (new_colors[i][x] == new_colors[j][y]) { graph[i << 1 | x].PB(j << 1 | (y ^ 1)); graph[j << 1 | y].PB(i << 1 | (x ^ 1)); } } } in.reset(); went.reset(); for (int i : small) if (ans[i] == -1) { if (!went[i << 1]) tarjan(i << 1); if (!went[i << 1 | 1]) tarjan(i << 1 | 1); if (scc[i << 1] == scc[i << 1 | 1]) return false; } for (int i : small) if (ans[i] == -1) { if (scc[i << 1] < scc[i << 1 | 1]) ans[i] = new_colors[i][0]; else ans[i] = new_colors[i][1]; } return true; } bool dfs(int idx) { if (idx >= int(big.size())) return check(); else { for (int color : colors[big[idx]]) { ans[big[idx]] = color; if (dfs(idx + 1)) return true; ans[big[idx]] = -1; } return false; } } int main() { //ios::sync_with_stdio(false); //cin.tie(0); //freopen("file_name", "r", stdin); //freopen("file_name", "w", stdout); //fstream input, output; //input.open("file_name", ios::in); //output.open("file_name", ios::out); int n, m; cin >> n >> m; for (int i = 1; i <= m; i++) cin >> a[i] >> b[i] >> c[i]; for (int i = 1; i <= n; i++) { int t; cin >> t; for (int j = 1; j <= t; j++) { char k; cin >> k; bs[i][k - 'a'] = true; } } for (int i = 1; i <= n; i++) p[i] = i; for (int i = 1; i <= m; i++) if (c[i] == 1) Merge(a[i], b[i]); for (int i = 1; i <= n; i++) if (p[i] == i && bs[i].none()) goto Fault; for (int i = 1; i <= n; i++) if (p[i] == i) { if (bs[i].count() >= 3) big.PB(i); else small.PB(i); } for (int i : big) is_big[i] = true; for (int i = 1; i <= m; i++) if (c[i] == 0) { int l = Find(a[i]), r = Find(b[i]); if (is_big[r]) big_edge[l].PB(r); else small_edge[l].PB(r); if (is_big[l]) big_edge[r].PB(l); else small_edge[r].PB(l); } for (int i = 1; i <= n; i++) if (p[i] == i) for (int j = 0; j < 26; j++) if (bs[i][j]) colors[i].PB(j); for (int i : big) ans[i] = -1; if (!dfs(0)) goto Fault; for (int i = 1; i <= n; i++) cout << char(ans[Find(i)] + 'a'); cout << "\n"; return 0; Fault: cout << "Fault\n"; return 0; //input.close(); //output.close(); }