#include #include #include #include using namespace std; // Combination consists of chosen words and present letters bitfield struct combination { unsigned short a, b, c, d; unsigned long present; combination* next; // Next non-useless combination (see find_useless) }; bool operator < ( const combination& u, const combination& v ) { return u.present > v.present; } const short NONE = -1; // I'm aware of the signed -> unsigned conversion const unsigned long ALL = (1 << 26) - 1; int optimize( int ); void find_useless( vector&, vector& ); int main( ) { int i, j; vector words; vector one, onep, two, twop, three, threep, four; // Read input, compute "present" bitfields cerr << "Reading in single words." << endl; string word; for( i = 0; cin >> word; i++ ) { combination c = {i, NONE, NONE, NONE, 0, NULL}; for( j = 0; j < word.length(); j++ ) { if( word[j] >= 'A' && word[j] <= 'Z' ) word[j] += 'a' - 'A'; if( word[j] < 'a' || word[j] > 'z' ) continue; c.present |= 1 << (word[j] - 'a'); } words.push_back( word ); one .push_back( c ); } cerr << one.size() << " single words." << endl; // Find useless words cerr << "Finding all useful single words." << endl; find_useless( one, onep ); cerr << onep.size() << " useful single words." << endl; one = vector(); for( i = 0; i < onep.size(); i++ ) cout << words[onep[i].a] << "\n"; // Compute all doubles now cerr << "Listing all pairs of words.\n"; for( i = 0; i < onep.size(); i++ ) { for( j = i+1; j < onep.size(); j++ ) { combination c = {onep[i].a, onep[j].a, NONE, NONE, onep[i].present | onep[j].present, NULL}; two.push_back( c ); } } cerr << two.size() << " total pairs." << endl; // Find useless two-word combinations cerr << "Finding all useful pairs." << endl; find_useless( two, twop ); cerr << twop.size() << " useful pairs." << endl; two = vector(); for( i = 0; i < twop.size(); i++ ) cout << words[twop[i].a] << " " << words[twop[i].b] << "\n"; // Compute all triples now cerr << "Listing all triples of words." << endl; for( i = 0; i < onep.size(); i++ ) { for( j = 0; j < twop.size(); j++ ) { if( onep[i].a == twop[j].a || onep[i].a == twop[j].b ) continue; combination c = {onep[i].a, twop[j].a, twop[j].b, NONE, onep[i].present | twop[j].present, NULL}; three.push_back( c ); } } cerr << three.size() << " total triples." << endl; // Find useless three-word combinations cerr << "Finding all useful triples.\n"; find_useless( three, threep ); cerr << threep.size() << " useful triples.\n"; three = vector(); for( i = 0; i < threep.size(); i++ ) cout << words[threep[i].a] << " " << words[threep[i].b] << " " << words[threep[i].c] << "\n"; // Find four-letter combination spanning everything cerr << "Finding a single universal quadruple.\n"; for( i = 0; i < twop.size(); i++ ) { for( j = 0; j < twop.size(); j++ ) { if( (twop[i].present | twop[j].present) == ALL ) break; } if( j < twop.size() ) break; } if( i < twop.size() ) { cout << words[twop[i].a] << " " << words[twop[i].b] << " " << words[twop[j].a] << " " << words[twop[j].b] << "\n"; cerr << "Universal quadruple found." << endl; } else { cerr << "Universal quadruple not found." << endl; } return 0; } void find_useless( vector& in, vector& out ) { int i; // Sort (decreasing) by present sort( in.begin(), in.end() ); // String together nexts for( i = 0; i+1 < in.size(); i++ ) in[i].next = &in[i+1]; // Find useless words combination *c, *d; for( c = &in[0]; c->next != NULL; c = c->next ) { for( d = c; d->next != NULL; ) { if( (d->next->present & ~c->present) == 0 ) { d->next = d->next->next; } else { d = d->next; } } if( c->next == NULL ) break; } // Output for( c = &in[0]; c != NULL; c = c->next ) out.push_back( *c ); }