#include #include #include #include #include #define CHARS_MAX 4*1024 #define MAX_PATTERNS 1024 #define MAX_PATTERN_ELEMENTS 1024 #define MAX_DESIGNS 1024 #define MAX_DESIGN_ELEMENTS 1024 #define COLOR_COUNT 5 #define cti(a) ((a) == 'w' ? 0 : (((a) == 'u' ? 1 : ((a) == 'b' ? 2 : ((a) == 'r' ? 3 : 4))))) typedef struct sequence { char *colors; int count; } sequence_t; typedef struct node { struct node *children[COLOR_COUNT]; uint64_t variants; char key; uint8_t is_leaf; } node_t; node_t* create_node() { node_t *node = calloc(1, sizeof(node[0])); return node; } node_t* search(node_t* root, char* word, int len) { node_t* node = root; for (int i = 0; i < len; i++) { if (node->children[cti(word[i])] == NULL) { return NULL; } node = node->children[cti(word[i])]; } return node; } void insert(node_t* root, char* word, int len, uint64_t variants) { node_t* node = root; for (int i = 0; i < len; i++) { if (node->children[cti(word[i])] == NULL) { node->children[cti(word[i])] = create_node(); } node = node->children[cti(word[i])]; } node->variants = variants; node->is_leaf = 1; } void free_node(node_t* node) { for (int i = 0; i < COLOR_COUNT; i++) { if (node->children[i] != NULL) { free_node(node->children[i]); } } free(node); } int possible(sequence_t design, node_t *pattern_trie); uint64_t possible_variants(sequence_t design, node_t* pattern_trie, node_t* variant_trie); int main() { char *p, *buf, c; buf = calloc(CHARS_MAX, sizeof(char)); p = buf; sequence_t *designs = calloc(MAX_DESIGNS, sizeof(designs[0])); for (int i = 0; i < MAX_DESIGNS; i++) { designs[i].colors = calloc(MAX_DESIGN_ELEMENTS, sizeof(designs[0].colors[0])); } int design_count = 0; sequence_t *patterns = calloc(MAX_PATTERNS, sizeof(patterns[0])); for (int i = 0; i < MAX_PATTERNS; i++) { patterns[i].colors = calloc(MAX_PATTERN_ELEMENTS, sizeof(patterns[0].colors[0])); } int pattern_count = 0; int reading_available_patterns = 1; while ((c = getchar()) != EOF) { if (reading_available_patterns) { *p++ = c; if (c != '\n') { continue; } if (buf[0] == '\n') { reading_available_patterns = 0; continue; } char *q = p; p = buf; do { while (*p != '\n' && *p != ',') { patterns[pattern_count].colors[patterns[pattern_count].count++] = *p; p++; } pattern_count++; p += 2; } while (p < q); memset(buf, 0, CHARS_MAX); p = buf; } else { if (c == '\n') { design_count++; continue; } designs[design_count].colors[designs[design_count].count++] = c; } } uint32_t shortest_pattern = UINT32_MAX, longest_pattern = 0; for (int i = 0; i < pattern_count; i++) { if (patterns[i].count < shortest_pattern) { shortest_pattern = patterns[i].count; } if (patterns[i].count > longest_pattern) { longest_pattern = patterns[i].count; } } node_t *pattern_trie = calloc(1, sizeof(pattern_trie[0])); for (int i = 0; i < pattern_count; i++) { sequence_t current_sequence = { 0 }; current_sequence.colors = calloc(MAX_DESIGN_ELEMENTS, sizeof(current_sequence.colors[0])); insert(pattern_trie, patterns[i].colors, patterns[i].count, 0); free(current_sequence.colors); } int part1 = 0; uint64_t part2 = 0; // Try to build each design out of the available patterns #pragma omp parallel for for (int i = 0; i < design_count; i++) { sequence_t current_sequence = { 0 }; current_sequence.colors = calloc(MAX_DESIGN_ELEMENTS, sizeof(current_sequence.colors[0])); if (possible(designs[i], pattern_trie)) #pragma omp critical { part1++; } free(current_sequence.colors); } node_t *variant_trie = calloc(1, sizeof(variant_trie[0])); for (int i = 0; i < design_count; i++) { sequence_t current_sequence = { 0 }; current_sequence.colors = calloc(MAX_DESIGN_ELEMENTS, sizeof(current_sequence.colors[0])); part2 += possible_variants(designs[i], pattern_trie, variant_trie); free(current_sequence.colors); } free(buf); for (int i = 0; i < MAX_DESIGNS; i++) { free(designs[i].colors); } free(designs); for (int i = 0; i < MAX_PATTERNS; i++) { free(patterns[i].colors); } free(patterns); free_node(pattern_trie); free_node(variant_trie); printf("%i\n", part1); printf("%lu\n", part2); } int possible(sequence_t design, node_t *pattern_trie) { node_t *node = search(pattern_trie, design.colors, design.count); if (node != NULL) { return 1; } for (int i = 1; i <= design.count; i++) { char *pattern = calloc(i, sizeof(pattern[0])); memcpy(pattern, design.colors, i*sizeof(pattern[0])); node_t *node = search(pattern_trie, pattern, i); free(pattern); if (node == NULL || node->is_leaf == 0) { continue; } sequence_t subdesign = { 0 }; subdesign.count = design.count - i; subdesign.colors = calloc(subdesign.count, sizeof(subdesign.colors[0])); memcpy(subdesign.colors, design.colors + i, subdesign.count*sizeof(subdesign.colors[0])); int good = possible(subdesign, pattern_trie); free(subdesign.colors); if (good) { return 1; } } return 0; } uint64_t possible_variants(sequence_t design, node_t* pattern_trie, node_t* variant_trie) { if (design.count == 0) { return 1; } char *pattern = calloc(design.count+1, sizeof(pattern[0])); memcpy(pattern, design.colors, (design.count+1)*sizeof(pattern[0])); pattern[design.count] = '\0'; node_t *node = search(variant_trie, design.colors, design.count); // We already know the answer if (node != NULL && node->is_leaf) { char *pattern = calloc(design.count+1, sizeof(pattern[0])); memcpy(pattern, design.colors, (design.count+1)*sizeof(pattern[0])); pattern[design.count] = '\0'; return node->variants; } uint64_t count = 0; // Try all possible patterns for (int i = 1; i <= design.count; i++) { char *pattern = calloc(i+1, sizeof(pattern[0])); memcpy(pattern, design.colors, (i+1)*sizeof(pattern[0])); node_t *node = search(pattern_trie, pattern, i); pattern[i] = '\0'; if (node == NULL || node->is_leaf == 0) { continue; } free(pattern); sequence_t subdesign = { 0 }; subdesign.count = design.count - i; subdesign.colors = calloc(subdesign.count, sizeof(subdesign.colors[0])); memcpy(subdesign.colors, design.colors + i, subdesign.count*sizeof(subdesign.colors[0])); count += possible_variants(subdesign, pattern_trie, variant_trie); free(subdesign.colors); } insert(variant_trie, design.colors, design.count, count); return count; }