Sunday, January 3, 2010

Chaining Game ( 2007 )

Problem :
One popular party game involves one person thinking of a word and then each person afterward thinking of a different word which relates in some way. In some versions of the game, the words are related by the letters in them; the first letter of the new word must be the same as the last letter of the old word. These kinds of constructions are called “word chains”, and the more words you use, the harder it gets to put them together.

Write a program that is able to take a list of words and determine if the entire set of these words could be used in a single word chain, with each word used exactly once. For instance, the words “Carpenter”, “Thread”, and “Ratchet” would be a valid list of words, because they can be combined into a single chain (“CarpenteRatcheThread”), but “Yard”, “Denmark”, and “Cheese” would not, because “Cheese” can’t connect to either “Yard” or “Denmark”.

Input

Input will consist of several words, separated by spaces. These words should be able to interchange uppercase and lowercase freely; “cabin” should be processed identically to “CaBiN”.

Output

The output can take one of two forms. If the program finds that it is impossible to create a chain, it should simply print out the word “Impossible”. If a chain is found, the program should print out this chain, in order. Let the case of the connecting letter be determined by the word on the right.

Sample input

Carpenter threaD RatcheT

Sample output

CarpenteRatchethreaD

Solution :
The simplest solution that I used is "permutation" and then check the last character of the word at index i with the first character of the word index i + 1.

#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <climits>
#include <algorithm>
#include <cctype>

using namespace std;


vector< string > util_tokenize_string( const string& str, const string& del = " " ) {
vector< string > result;
unsigned start = 0;
unsigned end = str.find_first_of( del, start );
unsigned length = str.length();
string word;
while( string::npos != end && end < length ) {
word = str.substr( start, end - start );
result.push_back( word );
start = end + 1;
end = str.find_first_of( del, start );
}

end = str.find_last_of( del ) + 1;
result.push_back( str.substr( end ) );

return result;
}

vector< string > read_file( const char* file_name ) {
ifstream inf( file_name );
string line;
getline( inf, line );
return util_tokenize_string( line );
}

void check_chaining_words( vector< string >& w ) {
int len = w.size();
vector< string > input = w;
string output;

do {
bool is_it = true;
next_permutation( w.begin(), w.begin() + len );
output += w[ 0 ].substr( 0, w[ 0 ].length() - 1 );
for( int i = 0; i < len - 1; ++i ) {
if( i + 1 != len - 1 )
output += w[ i + 1 ].substr( 0, w[ i + 1 ].length() - 1 );
else
output += w[ i + 1 ];

if( toupper( w[ i ][ w[ i ].size() - 1 ] ) != toupper( w[ i + 1 ][ 0 ] ) ) {
is_it = false;
break;
}
}
if( is_it == true ) {
cout << output << endl;
} else {
output = "";
}

} while( w != input );
}

int main() {
vector< string > w = read_file( "love.txt" );
check_chaining_words( w );

return 0;
}

Friday, December 25, 2009

Word Index ( 2009 )

This problem is from ProgFest Contest 2009( 02/14/2009 ). During the contest, I got stuck on this problem for 2 hours, really really dumb when I thought over it. It is probably one of the easiest problems that I have missed T_T !

Problem :

Consider the English alphabet {a,b,c,...z}. Using this alphabet, a set of valid words is to be formed that are in a strict lexicographic order. In this set of valid words, the successive letters of a word are in a strictly ascending order; that is, later letters in a valid word are always after previous letters with respect to their positions in the alphabet list {a,b,c...,z}. For example,
abc aep gwz
are all valid three-letter words, whereas
aab are cat
are not.

For each valid word associate an integer which gives the position of the word in the alphabetized list of words. That is:
a --> 1
b --> 2
.
.
z --> 26
ab --> 27
ac --> 28
.
.
az --> 51
bc --> 52
.
.
vwxyz --> 83681
Your program is to read a series of input lines. Each input line will have a single word on it, that will be from one to five letters long. For each word read, if the word is invalid give the number 0. If the word read is valid, give the word's position index in the above alphabetical list.
Input
The input consists of a series of single words, one per line. The words are at least one letter long and no more that five letters. Only the lower case alphabetic {a,b,...,z} characters will be used as input. The first letter of a word will appear as the first character on an input line.
The input will be terminated by end-of-file.
Output
The output is a single integer, greater than or equal to zero (0) and less than or equal 83681. The first digit of an output value should be the first character on a line. Note: This may not be a default-format. There is one line of output for each input line.

Sample Input
z
a
cat
vwxyz

Sample Output

26
1
0
83681

Solution :

#include <string>
#include <iostream>
#include <sstream>
#include <map>

using namespace std;

bool is_valid( const string& s ) {
for( int i = 1; i < s.length(); ++i ) {
if( s[ i ] <= s[ i - 1 ] )
return false;
}
return true;
}

map< string, int > generate() {
char previous_char;
int index = 1;
map< string, int > m;

int count = 0;
for( char i1 = 'a'; i1 <= 'z'; ++i1 ) {
count++;
ostringstream o;
o << i1;
m.insert( pair< string, int >( o.str(), count ) );
}

for( char i1 = 'a'; i1 <= 'z'; ++i1 ) {
for( char i2 = i1 + 1; i2 <= 'z'; ++i2 ) {
count++;
ostringstream o;
o << i1 << i2;
m.insert( pair< string, int >( o.str(), count ) );

}
}

for( char i1 = 'a'; i1 <= 'z'; ++i1 ) {
for( char i2 = i1 + 1; i2 <= 'z'; ++i2 ) {
for( char i3 = i2 + 1; i3 <= 'z'; ++i3 ) {
count++;
ostringstream o;
o << i1 << i2 << i3;
m.insert( pair< string, int >( o.str(), count ) );
}
}
}

for( char i1 = 'a'; i1 <= 'z'; ++i1 ) {
for( char i2 = i1 + 1; i2 <= 'z'; ++i2 ) {
for( char i3 = i2 + 1; i3 <= 'z'; ++i3 ) {
for( char i4 = i3 + 1; i4 <= 'z'; ++i4 ) {
count++;
ostringstream o;
o << i1 << i2 << i3 << i4;
m.insert( pair< string, int >( o.str(), count ) );
}
}
}
}

for( char i1 = 'a'; i1 <= 'z'; ++i1 ) {
for( char i2 = i1 + 1; i2 <= 'z'; ++i2 ) {
for( char i3 = i2 + 1; i3 <= 'z'; ++i3 ) {
for( char i4 = i3 + 1; i4 <= 'z'; ++i4 ) {
for( char i5 = i4 + 1; i5 <= 'z'; ++i5 ) {
count++;
ostringstream o;
o << i1 << i2 << i3 << i4 << i5;
m.insert( pair< string, int >( o.str(), count ) );
}
}
}
}
}

return m;
}

int main() {
map< string, int > m = generate();
string input;
while( cin >> input ) {
if( is_valid( input ) == false )
cout << "0" << endl;
else
cout << m[ input ] << endl;
}
return 0;
}


Thursday, September 24, 2009

Credit Card Processing

Credit Card Processing from Problem Set 2005-2006

http://www.socalcontest.org/current/index.shtml

Solution :

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <fstream>
#include <cctype>
#include <sstream>
#include <vector>

using namespace std;

string util_trim_right( const string& s, const string& delim = " " ) {
string temp( s );
string::size_type o = temp.find_last_not_of( delim );
if ( o == string::npos )
return temp;
else
return temp.erase( temp.find_last_not_of( delim ) + 1 );
}

string util_trim_left( const string& s, const string& delim = " " ) {
string temp( s );
return temp.erase( 0, s.find_first_not_of( delim ) );
}

vector< string > util_tokenize_string( const string& str, const string& del = "," ) {
vector< string > result;
string::size_type start = 0;
string::size_type end = str.find_first_of( del, start );
string::size_type length = str.length();
string word;

while ( string::npos != end && end < length ) {
word = str.substr( start, end - start );
result.push_back( word );
start = end + 1;
end = str.find_first_of( del, start );
}

end = str.find_last_of( del ) + 1;
result.push_back( str.substr( end ) );

return result;
}

void util_delete_all_white_space( string& s ) {
s.erase( remove( s.begin(), s.end(), ' ' ), s.end() );
}

bool util_is_all_white_space( const string& s ) {
for ( unsigned o = 0, o_end = s.length(); o < o_end; ++o )
if ( s[ o ] != ' ' )
return false;

return true;
}


class Customer {
public :
string cu_first_name;
string cu_last_name;
string cu_address;
string cu_city;
string cu_state;
string cu_zipcode;
string cu_ssn;
string cu_credit_1;
string cu_credit_2;
string cu_credit_3;
char cu_literal_semicolon;

public :
// bussiness rule 1
bool cu_test_leading_white_space_and_null() const;

// bussiness rule 2
bool cu_test_byte_field_isdigit_isalpha() const;

// bussiness rule 3
bool cu_test_credit_score() const;

// bussiness rule 4
bool cu_test_credit_score_mean() const;

void cu_show_customer_info() const;

};


void Customer::cu_show_customer_info() const {
cout << "1." << cu_first_name << "\n";
cout << "2." << cu_last_name << "\n";
cout << "3." << cu_address << "\n";
cout << "4." << cu_city << "\n";
cout << "5." << cu_state << "\n";
cout << "6." << cu_zipcode << "\n";
cout << "7." << cu_ssn << "\n";
cout << "8." << cu_credit_1 << "\n";
cout << "9." << cu_credit_2 << "\n";
cout << "10." << cu_credit_3 << "\n";
}

bool Customer::cu_test_credit_score() const {
istringstream o1( cu_credit_1, istringstream::in );
istringstream o2( cu_credit_2, istringstream::in );
istringstream o3( cu_credit_3, istringstream::in );

int val_1, val_2, val_3;
o1 >> val_1;
o2 >> val_2;
o3 >> val_3;

if (
val_1 < 400 ||
val_2 < 400 ||
val_3 < 400 ) {

return false;
}

return true;
}

bool Customer::cu_test_credit_score_mean() const {
istringstream o1( cu_credit_1, istringstream::in );
istringstream o2( cu_credit_2, istringstream::in );
istringstream o3( cu_credit_3, istringstream::in );

int val_1, val_2, val_3;
o1 >> val_1;
o2 >> val_2;
o3 >> val_3;

double mean = static_cast< double >( val_1 + val_2 + val_3 ) / 3.0;
if ( mean < 500 ) {
return false;
}

return true;
}

bool Customer::cu_test_byte_field_isdigit_isalpha() const {

// check literal_semicolon
if ( cu_literal_semicolon != ';' )
return false;

// check byte field
if ( cu_state.length() != 2 || cu_zipcode.length() != 5 )
return false;

// check city for non-alphanumeric
for ( unsigned o = 0, o_end = cu_city.length(); o < o_end; ++o ) {
if ( isdigit( cu_city[ o ] ) == false && isalpha( cu_city[ o ] ) == false )
return false;
}

// check state for only alphabetic
for ( unsigned o = 0, o_end = cu_state.length(); o < o_end; ++o ) {
if ( isalpha( cu_state[ o ] ) == false )
return false;
}

// check zipcode for only numeric
for ( unsigned o = 0, o_end = cu_zipcode.length(); o < o_end; ++o ) {
if ( isdigit( cu_zipcode[ o ] ) == false )
return false;
}

// check ssn for only numeric
for ( unsigned o = 0, o_end = cu_ssn.length(); o < o_end; ++o ) {
if ( isdigit( cu_ssn[ o ] ) == false )
return false;
}

unsigned CREDIT_DIGITS = 3;
// check credit_1, credit_2, credit_3 for only numeric
for ( unsigned o = 0; o < CREDIT_DIGITS; ++o ) {
if (
isdigit( cu_credit_1[ o ] ) == false ||
isdigit( cu_credit_2[ o ] ) == false ||
isdigit( cu_credit_3[ o ] ) == false ) {

return false;
}
}

// check credit_1, credit_2, credit_3 for valid range ( 200 - 800 )
istringstream o1( cu_credit_1, istringstream::in );
istringstream o2( cu_credit_2, istringstream::in );
istringstream o3( cu_credit_3, istringstream::in );

int val_1, val_2, val_3;
o1 >> val_1;
o2 >> val_2;
o3 >> val_3;

if (
val_1 < 200 || val_1 > 800 ||
val_2 < 200 || val_2 > 800 ||
val_3 < 200 || val_3 > 800 ) {
return false;
}

return true;
}

bool Customer::cu_test_leading_white_space_and_null() const {

// check for NULL character
if ( cu_literal_semicolon == ' ' )
return false;

// check for NULL string

if (
util_is_all_white_space( cu_first_name ) == true ||
util_is_all_white_space( cu_last_name ) == true ||
util_is_all_white_space( cu_address ) == true ||
util_is_all_white_space( cu_city ) == true ||
util_is_all_white_space( cu_state ) == true ||
util_is_all_white_space( cu_zipcode ) == true ||
util_is_all_white_space( cu_ssn ) == true ||
util_is_all_white_space( cu_credit_1 ) == true ||
util_is_all_white_space( cu_credit_2 ) == true ||
util_is_all_white_space( cu_credit_3 ) == true ) {

return false;
}

// check for leading white space
if (
cu_first_name[ 0 ] == ' ' ||
cu_last_name[ 0 ] == ' ' ||
cu_address[ 0 ] == ' ' ||
cu_city[ 0 ] == ' ' ||
cu_state[ 0 ] == ' ' ||
cu_zipcode[ 0 ] == ' ' ) {

return false;
}

return true;
}

vector< Customer > main_prog_get_data_from_file( const char* file_name ) {
ifstream o( file_name );
string oneline;
vector< Customer > cus_vec;

while ( getline( o, oneline ) ) {

cout << oneline << endl;
Customer cus;
string temp; // use for a_c_s_z string

// get the last character
cus.cu_literal_semicolon = oneline[ 89 ];

for ( unsigned o = 0; o < 12; ++o ) {
cus.cu_first_name.push_back( oneline[ o ] );
}

for ( unsigned o = 12; o < 29; ++o ) {
cus.cu_last_name.push_back( oneline[ o ] );
}

for ( unsigned o = 30; o < 71; ++o ) {
temp.push_back( oneline[ o ] );
}

for ( unsigned o = 71; o < 79; ++o ) {
cus.cu_ssn.push_back( oneline[ o ] );
}

for ( unsigned o = 80; o < 83; ++o ) {
cus.cu_credit_1.push_back( oneline[ o ] );
}

for ( unsigned o = 83; o < 86; ++o ) {
cus.cu_credit_2.push_back( oneline[ o ] );
}

for ( unsigned o = 86; o < 89; ++o ) {
cus.cu_credit_3.push_back( oneline[ o ] );
}


/* trim right for :
1. first name
2. last name
*/
cus.cu_first_name = util_trim_right( cus.cu_first_name );
cus.cu_last_name = util_trim_right( cus.cu_last_name );

/*
Since we need to have enough commas( 3 )
to handle the tokenizer, we need to test :
1. All whitespace
2. No comma
3. Not enough comma

One of these 3 fails, we will set all the 4 field :
- cu_address
- cu_city
- cu_state
- cu_zipcode
to all whitespace so it will fail later on
business rule 1
*/
bool is_all_white_space = true;
for ( unsigned o = 0, o_end = temp.length(); o < o_end; ++o ) {
if ( temp[ o ] != ' ' )
is_all_white_space = false;
}

bool is_no_comma_separator = true;
for ( unsigned o = 0, o_end = temp.length(); o < o_end; ++o ) {
if ( temp[ o ] == ',' )
is_no_comma_separator = false;
}

bool is_not_enough_commad = false;
unsigned comma_count = 0;
for ( unsigned o = 0, o_end = temp.length(); o < o_end; ++o ) {
if ( temp[ o ] == ',' )
comma_count++;
}

if ( comma_count < 3 )
is_not_enough_commad = true;

if ( is_no_comma_separator == true ||
is_all_white_space == true ||
is_no_comma_separator == true ) {
fill_n( cus.cu_address.begin(), cus.cu_address.length(), ' ' );
fill_n( cus.cu_city.begin(), cus.cu_city.length(), ' ' );
fill_n( cus.cu_state.begin(), cus.cu_state.length(), ' ' );
fill_n( cus.cu_zipcode.begin(), cus.cu_zipcode.length(), ' ' );
} else {

vector< string > address_city_state_zipcode;
address_city_state_zipcode = util_tokenize_string( temp );
/* trim right for :
3. address
4. city
5. state
6. zipcode
*/
cus.cu_address = util_trim_right( address_city_state_zipcode[ 0 ] );
cus.cu_city = util_trim_right( address_city_state_zipcode[ 1 ] );
cus.cu_state = util_trim_right( address_city_state_zipcode[ 2 ] );
cus.cu_zipcode = util_trim_right( address_city_state_zipcode[ 3 ] );
}

/* delete all white space for :
1. ssn
2. credit 1
3. credit 2
4. credit 3
*/
util_delete_all_white_space( cus.cu_ssn );
util_delete_all_white_space( cus.cu_credit_1 );
util_delete_all_white_space( cus.cu_credit_2 );
util_delete_all_white_space( cus.cu_credit_3 );


cus_vec.push_back( cus );
}

return cus_vec;
}

void main_prog_test_all_customers( const vector< Customer >& v ) {
for ( unsigned o = 0, o_end = v.size(); o < o_end; ++o ) {
cout << "\n\n --- Customer " << o + 1 << "\n";

bool status = true;
status = v[ o ].cu_test_leading_white_space_and_null();
cout << "Status 1 " << status << endl;

status = v[ o ].cu_test_byte_field_isdigit_isalpha();
cout << "Status 2 " << status << endl;

status = v[ o ].cu_test_credit_score();
cout << "Status 3 " << status << endl;

status = v[ o ].cu_test_credit_score_mean();
cout << "Status 4 " << status << endl;
}
}

int main() {

vector< Customer > v;
v = main_prog_get_data_from_file( "in1.txt" );
main_prog_test_all_customers( v );
return 0;
}