C++ linked list implementation multiple data insertion - c++
I'm trying to create a subject pre-requisite checker using linked list. I know how to insert single data into a node.
My problem is how to insert multiple data into a node? I found a good example which fits my assignment perfectly. But the problem is I do not understand C very much. Can any one help to explain the void add() function below? I want to use that add function to my assignment.
#include <stdio.h>
#include <conio.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
struct node
{
char data [ 20 ];
char m [ 5 ] [ 20 ];
int mcount;
struct node * link;
};
struct node * dic [ 26 ];
void add ( char * );
int search ( char * );
void show( );
void deldic( );
void main( )
{
char word [ 20 ] , ch;
int i;
clrscr( );
while ( 1 )
{
clrscr( );
printf ( "\n\t\tDictionary\n" );
printf ( "\n\t\t1.Add Word.\n" );
printf ( "\t\t2.Search Word.\n" );
printf ( "\t\t3.Show Dictionary.\n" );
printf ( "\t\t0.Exit." );
printf ( "\n\n\t\tYour Choice ");
scanf ( "%d", &ch );
switch ( ch )
{
case 1 :
printf ( "\nEnter any word : " );
fflush ( stdin );
gets ( word );
add ( word );
break;
case 2 :
printf ( "\nEnter the word to search : " );
fflush ( stdin );
gets ( word );
i = search ( word );
if ( ! i )
printf ( "Word does not exists." );
getch( );
break;
case 3 :
show( );
getch( );
break;
case 0 :
deldic( );
exit ( 0 );
default :
printf ( "\nWrong Choice" );
}
}
}
void add ( char * str )
{
int i, j = toupper ( str [ 0 ] ) - 65;
struct node * r, * temp = dic [ j ], * q;
char mean [ 5 ] [ 20 ], ch = 'y';
i = search ( str );
if ( i )
{
printf ( "\nWord already exists." );
getch( );
return;
}
q = ( struct node * ) malloc ( sizeof ( struct node ) );
strcpy ( q -> data, str );
q -> link = NULL;
for ( i = 0; tolower ( ch ) == 'y' && i < 5; i++ )
{
fflush ( stdin );
printf ( "\n\nEnter the meaning(s) : " );
gets ( mean [ i ] );
strcpy ( q -> m [ i ] , mean [ i ] );
if ( i != 4 )
printf ( "\nAdd more meanings (y/n) " );
else
printf ( "You cannot enter more than 5 meanings." );
fflush ( stdin );
ch = getche( );
}
q -> mcount = i;
if ( dic [ j ] == NULL || strcmp ( dic [ j ] -> data, str ) > 0 )
{
r = dic [ j ];
dic [ j ] = q;
q -> link = r;
return;
}
else
{
while ( temp != NULL )
{
if ( ( strcmp ( temp -> data, str ) < 0 ) && ( ( strcmp ( temp -> link -> data, str ) > 0 ) ||
temp -> link == NULL ) )
{
q -> link = temp -> link;
temp -> link = q;
return;
}
temp = temp -> link;
}
}
}
Here is my assignment so far
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
struct subjectlist
{
string subject;
string prereq;
subjectlist *next;
};
subjectlist *start_prt=NULL;
subjectlist *current;
int option=0;
int main ()
{
int x;
string subject;
cout << "1. Add subject" << endl;
cout << "2. Search prerequisite" << endl;
cout << "3. Delete subject" << endl;
cout << "4.Show subjects" << endl;
cout << "5. Save to file" << endl;
cout << "6. Load from file" << endl;
cout << "0. Exit" << endl;
cin >> x;
switch (x)
{
case 1:
cout<<"Input subject"<<endl;
cin >> subject;
add(subject);
break;
case 2:
cout<<"Input subject to be checked"<<endl;
break;
case 3:
cout<<"Delete a subject"<<endl;
break;
case 4:
cout<<"Show Subjects"<<endl;
break;
case 5:
cout<<"Save to File"<<endl;
break;
case 6:
cout<<"Load from file"<<endl;
break;
case 0:
cout<<"exit"<<endl;
break;
default: cout <<"Invalid selection, please try again."<<endl;
}
}
void add ()
{
}
The add() function adds node to list. But before adding node to list, it checks whether the data in node is already present in list?
i = search ( str );
if ( i )
This checks for duplicate data.
If data is already present in list, node is not inserted in list.
If data is not present in list, it moves further.
for ( i = 0; tolower ( ch ) == 'y' && i < 5; i++ )
This for loop accepts meaning (string) in array and only 5 meanings can be added per node.
Also node is added to list in such a way that list will be in sorted form.
Since you are working with c++, a language, what supports object oriented programming, why not use this feature?
First you could create your data structure, what contains all the useful items, you want to store. You could also write an operator== what makes MUCH clearer to compare two Data objects:
struct Data
{
char data [20];
char m [5][20];
int mcount;
bool operator==(const Data& other)const
{
//probably you need more comparisons
return mcount==other.mcount;
}
};
Then you could create a Node class, what holds one of your Data objects, and a pointer to the next (maybe to the previous) item.
struct Node
{
Data data;
Node * next;
//Node * previous;
}
After you got this, you could just create your own linked list class:
class MyLinkedList
{
Node * head;
public:
MyLinkedList(){//initialization steps}
~MyLinkedList(){ //Delete the list}
void add(Data item)
{
if(!contains(item))
{
//append it
}
}
bool contains(Data item){ //... check if the list already contains item}
//create a string representation of the object.
//If you dont like this style, you could also provide
//an operator>> or operator<< for the class
std::string toString()
{
std::stringstream stream;
//iterate through the list, and add elements with
return stream.str();
}
};
If you got this, then in your main() it would look much clearer, what you want:
MyLinkedList list;
Data data; //somehow fill it
//adding items
list.add(data);
//printing the list
cout<<list.toString();
//after it goes out of scope the destructor will be called,
//so you dont need to bother with the deletion.
Related
scoped_ptr to call member function throws error
I am currently reading Accelerated C++ ch13 and thought of doing sample program given in book via boost scoped_ptr but have encountered an error. May you guys please bail me out. ** ***error: cannot use arrow operator on a type record->read( cin );*** ^ ** Original sample code is something shown below and this works flawlessly std::vector< Core* > students ; // read students Core* records; std::string::size_type maxlen = 0; // read data and store in an object char ch ; while( cin >> ch ) { if( 'U' == ch ) { records = new Core; } else if( 'G' == ch ) { records = new Grad; } records->read( cin ); maxlen = max( maxlen , records->getname().size() ); students.push_back( records ); } Now using scoped_ptr MY VERSION typedef boost::scoped_ptr<Core> record; std::vector< record > students; char ch; std::string::size_type maxlen = 0; // read and store while( ( cin >> ch ) ) { if( ch == 'U') { record( new Core); } else if( ch == 'G') { record( new Grad); } record->read( cin );// GOT ERROR //maxlen = max( maxlen, record->name().size() );// SAME type of error I EXPECT HERE // students.push_back( record ); }
Not sure how to pass parameters from class to constructor in main()
My assignment is to read from a gradbook text file and average every students' program, midterm and final scores, as well as the student ID and the first and last name. So far, code is able to compile and calculates the scores appropriately, but it won't read the studentID and first and last name. I'm pretty sure the reason why is that the currentStudent variable in main() doesn't have any parameters and makes the constructor use default values. But I'm not sure how to give currentStudent values from class Student while in main(). My best idea for a solution is to move everything from ReadData into main(), but from the description of ReadData from my assignment, I think I have everything I need in there: "A method called ReadData(istream&) which reads the Student's data. It reads the ID number (integer), first and last names (strings) in that order, the 10 program scores (all integers) and the midterm and exam scores (also integers). It returns true if all data was read successfully, otherwise false" I'm sorry for the long-winded description, I'm just seeing if I can effectively explain my situation. Any help or advice would be greatly appreciated. I've only included the class definition, constructor, ReadData, and main below because everything else is just equations and get/sets that I'm pretty sure all work, and I'm trying to lessen what you lovely people would have to read through. If anyone would like to see the full code, I'll post the rest. #include <iostream> #include <fstream> #include <string> using namespace std; class Student { private: int studentID; string firstName, lastName; int score[ 10 ]; int midterm, final; public: Student ( int, string, string ); bool ReadData ( istream& ); //fstream WriteData ( ostream& ); // I need to clear up with professor first void setStudentID ( int ); void setFirstName ( string ); void setLastName ( string ); void setMidterm ( int ); void setFinal (int ); const int getStudentID ( ); const string getFirstName ( ); const string getLastName ( ); const int getMidterm ( ); const int getFinal ( ); void setProgramScore ( int, int[ ] ); int getProgramScore ( int ); const double ProgramAvg( ); const double CourseAvg( ); ~Student( ); }; Student::Student ( int id = 0, string f = "", string l = "" ) { setStudentID ( id ); setFirstName ( f ); setLastName ( l ); }; bool Student::ReadData( istream &readStudent ) { int id; string first, last; int x[ 10 ], mid, fin; readStudent >> id >> first >> last; for ( int i = 0; i <= 10 - 1; i++ ) { readStudent >> x [ i ]; setProgramScore( i, x ); } readStudent >> mid >> fin; Student studentInfo ( id, first, last ); setMidterm( mid ); setFinal( fin ); if ( readStudent.good( ) ) return true; else return false; }; // getters, setters and calculations in between int main( ) { ifstream readStudent; int lineCount = 0; double totalProgramAvg = 0 , totalFinalAvg = 0 , totalCourseAvg = 0; Student currentStudent; readStudent.open ( "gradebook.txt" ); if ( readStudent.is_open ( ) ) { while ( currentStudent.ReadData ( readStudent ) == true ) { totalProgramAvg += currentStudent.ProgramAvg(); totalFinalAvg += currentStudent.getFinal(); totalCourseAvg += currentStudent.CourseAvg(); cout << currentStudent.getStudentID() << " " << currentStudent.getFirstName() << " " << currentStudent.getLastName() << " "; for ( int j = 0; j < 10; j++ ) cout << currentStudent.getProgramScore( j ) << " "; cout << currentStudent.getMidterm() << " " << currentStudent.getFinal() << endl; cout << totalProgramAvg << " " << totalCourseAvg << endl; lineCount++; }; readStudent.close( ); cout << lineCount << endl << totalProgramAvg / lineCount << "\n" << totalFinalAvg / lineCount << "\n" << totalCourseAvg / lineCount; system ("pause"); }; };
bool Student::ReadData( istream &readStudent ) { int id; string first, last; int x[ 10 ], mid, fin; readStudent >> id >> first >> last; for ( int i = 0; i <= 10 - 1; i++ ) { readStudent >> x [ i ]; setProgramScore( i, x ); } readStudent >> mid >> fin; Student studentInfo ( id, first, last ); setMidterm( mid ); setFinal( fin ); if ( readStudent.good( ) ) return true; else return false; }; //what? I haven't checked the rest of your code, but this is certainly erroneous. You shouldn't be declaring a new item Student studentInfo( id, first, last); You're creating a new item that just dies when the function returns. Instead, you should use id,first,last to modify a current object member you're in, this. You have declared items for this in your class header but then declare local scope variables, use them, create a new student with it, and then all are destroyed when the function returns and they go out of scope. Simply delete/add things where I mark appropriate from the function to get bool Student::ReadData( istream &readStudent ) { int x[ 10 ], mid, fin; //if it ain't broke, don't fix it readStudent >> studentID >> firstName >> lastName; //use your class members that you want to hold that data. for ( int i = 0; i <= 10 - 1; i++ ) { readStudent >> x [ i ]; setProgramScore( i, x ); } readStudent >> mid >> fin; setMidterm( mid ); setFinal( fin ); if ( readStudent.good( ) ) return true; else return false; } You can directly access class members in the class function Student::ReadData( istream &readStudent) and you should just do it for all of them but you said the score system was working so I left that alone. Finally, ; goes after } if it's like a struct, or a class, or a bunch of stuff I don't know, but not a function definition. Okay, I see another bug /flaw in your project flow: while ( currentStudent.ReadData ( readStudent ) == true ) { /*stuff*/ } is not going to work right. Your ReadData function is going to read in all the data for your current student, but the while loop is also going to try to do that. I can't fathom the result but it will be ugly no doubt. Your better to use it like this: if(!(currentStudent.ReadData( readStudent)) { //Ooops, I failed, what do I do? }
To turn string into code
My english is not very good, but, I hope, you will understand me. I know that turning string into code is impossible in C++, but I just can't see another way of solving my trouble. I have an array of structures. You can say that it's a database. The task is that user should make combined request. In console a user chooses two parameters of structure and makes a combined request. Something like this cout<<"\nВыберите первый параметр для поиска:" // Choose first parameter <<"\n1. processor" <<"\n2. videocard" <<"\n3. display" <<"\n4. RAM" <<"\n5. size"<<endl; int first; cin>>first; cout<<"\nВыберите второй параметр для поиска:" // Choose second parameter <<"\n1. processor" <<"\n2. videocard" <<"\n3. display" <<"\n4. RAM" <<"\n5. size"<<endl; int second; cin>>second; cout<<"enter searchkey for first value: " string search1; cin>>search1; cout<<"enter searchkey for second value: " string search2; cin>>search2; string parameters[ 5 ] = { "processor", "videocard", "display", "RAM", "size" }; for ( i = 0; i < size; i++ ) // And that's where it all goes wrong. if ( arrayOfStructs.parameters[ first ] == search1 && arrayOfStructs.parameters[ second ] == search2 ) cout<<"found a match"<<endl; I know why code doesn't work. I'm really sure that exists a solution that looks similar to mine. My "solution" looks like enum, but enum in this case is not appropriate. If you know the solution, please write it down below. The full code of my program #include <iostream> #include <iomanip> #include <fstream> #include <string> #include <sstream> using namespace std; struct computer { string processor; string videocard; string display; string RAM; string size; string getFieldValue( int ); }; string computer::getFieldValue( int fieldNumber ) { stringstream str; switch (fieldNumber) { case 1: str<<this->processor; case 2: str<<this->videocard; case 3: str<<this->display; case 4: str<<this->RAM; case 5: str<<this->size; } return str.str(); } void coutAndWrite( computer aStruct, ofstream &aFile ); int main() { setlocale( 0, "" ); computer sklad[ 30 ]; computer temp; int i = 0, j; ifstream fromFile("structury.txt"); while ( !fromFile.eof() ) { fromFile>>sklad[ i ].processor >>sklad[ i ].videocard >>sklad[ i ].display >>sklad[ i ].RAM >>sklad[ i ].size; i++; } fromFile.close(); ofstream rezultaty("rezultaty.txt"); for ( i = 0; i < 30; i++ ) for ( j = 0; j < 29; j++ ) if ( sklad[ j + 1 ].processor[ 0 ] < sklad[ j ].processor[ 0 ] ) { temp = sklad[ j + 1 ]; sklad[ j + 1 ] = sklad[ j ]; sklad[ j ] = temp; } while ( 1 ) { cout<<"\nВыберите тип запроса:" <<"\n1. Простой" <<"\n2. Составной" <<"\n0. Выход из программы\n"; int prostoiIliSostavnoi; cin>>prostoiIliSostavnoi; if ( prostoiIliSostavnoi == 0 ) break; if ( prostoiIliSostavnoi == 1 ) { cout<<"\nВыберите параметр для поиска:" <<"\n1. processor" <<"\n2. videocard" <<"\n3. display" <<"\n4. RAM" <<"\n5. size"<<endl; int parametr; cin>>parametr; cout<<"Введите ключ поиска: "; string poisk; cin>>poisk; cout<<"Результаты поиска: "; for ( i = 0; i < 30; i++ ) if ( sklad[ i ].getFieldValue( parametr ) == poisk ) coutAndWrite( sklad[ i ], rezultaty ); } } system("pause"); } void coutAndWrite( computer aStruct, ofstream &aFile ) { cout<<"\nprocessor: "<<aStruct.processor <<"\nvideocard: "<<aStruct.videocard <<"\ndisplay: "<<aStruct.display <<"\nRAM: "<<aStruct.RAM <<"\nsize: "<<aStruct.size<<endl<<endl; aFile<<setw( 15 )<<aStruct.processor <<setw( 15 )<<aStruct.videocard <<setw( 20 )<<aStruct.display <<setw( 10 )<<aStruct.RAM <<setw( 10 )<<aStruct.size<<endl; }
Break it into pieces. Let's ignore the loop and the double search, and focus on the core problem: getting a field's value when you have its field number (or name): string Computer::getFieldValue(int fieldNumber) { stringstream str; switch (fieldNumber) { case 1: str << this->processor; break; case 2: str << this->videocard; break; case 3: str << this->display; break; case 4: str << this->RAM; break; case 5: str << this->size; break; } return str.str(); } Given this helper function, it's now possible to write the checking code. for (int i = 0; i < size; ++i) { if (computers[i].getFieldValue(first) == search1 && computers[i].getFieldValue(second) == search2) { cout << "found a match" << endl; } }
C++ Map exc_bad_access (Apple only)
Code Reads from On Windows 7 and 8 it runs fine. However, when running in XCode 4 I get EXC_BAD_ACCESS on the second iteration when someone loads a map (select "Load Map" from title). You can download the source with the XCode project #include <string> #include <map> #include <iostream> std::map <std::string, std::string> info; std::string* get_key_val( std::string* line ) { std::string key_val[2]; int start, end; start = line->find_first_not_of( " " ); end = line->find_last_of( ":" ); if( start == -1 ) { return NULL; } else if( end == -1 ) { return NULL; } else { key_val[0] = line->substr( start, end - start ); } start = line->find_first_not_of(" ", end + 1); end = line->find_last_of( " \n\r" ); if( start == -1 ) { return NULL; } else if( end == -1 ) { return NULL; } else { key_val[1] = line->substr( start, end - start ); } return key_val; } void parse_from_line( std::string* line ) { std::string* keyv = get_key_val( line ); if( keyv[0].empty() == false && keyv[1].empty() == false ) info[ keyv[0] ] = keyv[1]; } int main( int argc, char* args[] ) { std::string line = "name: Foo"; parse_from_line( &line ); std::cout << "Hello " << info["name"].c_str(); }
Your get_key_val function starts like this: std::string* Map::get_key_val( std::string* line ) { std::string key_val[2]; It ends like this: return key_val; } You're returning a pointer to a stack variable. The key_val variable ceases to exist upon return from the function, so you have an invalid pointer, and the two string values in the array get destroyed. Subsequent behavior is undefined.
With move semantics in C++11 onwards, its less necessary to do this. You can just return std::string and the move operator should avoid any wasteful copies.
String and character mapping question for the guru's out there
Here's a problem thats got me stumped (solution wise): Given a str S, apply character mappings Cm = {a=(m,o,p),d=(q,u),...} and print out all possible combinations using C or C++. The string can be any length, and the number of character mappings varies, and there won't be any mappings that map to another map (thus avoiding circular dependencies). As an example: string abba with mappings a=(e,o), d=(g,h), b=(i) would print: abba,ebba,obba,abbe,abbo,ebbe,ebbo,obbe,obbo,aiba,aiia,abia,eiba,eiia,......
Definitely possible, not really difficult... but this will generate lots of strings that's for sure. The first thing to remark is that you know how many strings it's going to generate beforehand, so it's easy to do some sanity check :) The second: it sounds like a recursive solution would be easy (like many traversal problems). class CharacterMapper { public: CharacterMapper(): mGenerated(), mMapped() { for (int i = -128, max = 128; i != max; ++i) mMapped[i].push_back(i); // 'a' is mapped to 'a' by default } void addMapped(char origin, char target) { std::string& m = mMapped[origin]; if (m.find(target) == std::string::npos) m.push_back(target); } // addMapped void addMapped(char origin, const std::string& target) { for (size_t i = 0, max = target.size(); i != max; ++i) this->addMapped(origin, target[i]); } // addMapped void execute(const std::string& original) { mGenerated.clear(); this->next(original, 0); this->sanityCheck(original); this->print(original); } private: void next(std::string original, size_t index) { if (index == original.size()) { mGenerated.push_back(original); } else { const std::string& m = mMapped[original[index]]; for (size_t i = 0, max = m.size(); i != max; ++i) this->next( original.substr(0, index) + m[i] + original.substr(index+1), index+1 ); } } // next void sanityCheck(const std::string& original) { size_t total = 1; for (size_t i = 0, max = original.size(); i != max; ++i) total *= mMapped[original[i]].size(); if (total != mGenerated.size()) std::cout << "Failure: should have found " << total << " words, found " << mGenerated.size() << std::endl; } void print(const std::string& original) const { typedef std::map<char, std::string>::const_iterator map_iterator; typedef std::vector<std::string>::const_iterator vector_iterator; std::cout << "Original: " << original << "\n"; std::cout << "Mapped: {"; for (map_iterator it = mMapped.begin(), end = mMapped.end(); it != end; ++it) if (it->second.size() > 1) std::cout << "'" << it->first << "': '" << it->second.substr(1) << "'"; std::cout << "}\n"; std::cout << "Generated:\n"; for (vector_iterator it = mGenerated.begin(), end = mGenerated.end(); it != end; ++it) std::cout << " " << *it << "\n"; } std::vector<std::string> mGenerated; std::map<char, std::string> mMapped; }; // class CharacterMapper int main(int argc, char* argv[]) { CharacterMapper mapper; mapper.addMapped('a', "eo"); mapper.addMapped('d', "gh"); mapper.addMapped('b', "i"); mapper.execute("abba"); } And here is the output: Original: abba Mapped: {'a': 'eo''b': 'i''d': 'gh'} Generated: abba abbe abbo abia abie abio aiba aibe aibo aiia aiie aiio ebba ebbe ebbo ebia ebie ebio eiba eibe eibo eiia eiie eiio obba obbe obbo obia obie obio oiba oibe oibo oiia oiie oiio Yeah, rather lengthy, but there's a lot that does not directly participate to the computation (initialization, checks, printing). The core methods is next which implements the recursion.
EDIT: This should be the fastest and simplest possible algo. Some may argue with the style or portability; I think this is perfect for an embedded-type thing and I've spent long enough on it already. I'm leaving the original below. This uses an array for mapping. The sign bit is used to indicate the end of a mapping cycle, so the array type has to be larger than the mapped type if you want to use the full unsigned range. Generates 231M strings/sec or ~9.5 cycles/string on a 2.2GHz Core2. Testing conditions and usage as below. #include <iostream> using namespace std; int const alphabet_size = CHAR_MAX+1; typedef int map_t; // may be char or short, small performance penalty int const sign_bit = 1<< CHAR_BIT*sizeof(map_t)-1; typedef map_t cmap[ alphabet_size ]; void CreateMap( char *str, cmap &m ) { fill( m, m+sizeof(m)/sizeof(*m), 0 ); char *str_end = strchr( str, 0 ) + 1; str_end[-1] = ' '; // space-terminated strings char prev = ' '; for ( char *pen = str; pen != str_end; ++ pen ) { if ( * pen == ' ' ) { m[ prev ] |= sign_bit; prev = 0; } m[ * pen ] = * pen; if ( prev != ' ' ) swap( m[prev], m[ *pen ] ); prev = *pen; } for ( int mx = 0; mx != sizeof(m)/sizeof(*m); ++ mx ) { if ( m[mx] == 0 ) m[mx] = mx | sign_bit; } } bool NextMapping( char *s, char *s_end, cmap &m ) { for ( char *pen = s; pen != s_end; ++ pen ) { map_t oldc = *pen, newc = m[ oldc ]; * pen = newc & sign_bit-1; if ( newc >= 0 ) return true; } return false; } int main( int argc, char **argv ) { uint64_t cnt = 0; cmap m; CreateMap( argv[1], m ); char *s = argv[2], *s_end = strchr( s, 0 ); do { ++ cnt; } while ( NextMapping( s, s_end, m ) ); cerr << cnt; return 0; } ORIGINAL: Not as short or robust as I'd like, but here's something. Requires that the input string always contain the alphabetically first letter in each replacement set Execute a la maptool 'aeo dgh bi' abbd Output is in reverse-lexicographical order Performance of about 22 cycles/string (100M strings/sec at 2.2 GHz Core2) BUT my platform is trying to be clever with strings, slowing it down If I change it to use char* strings instead, it runs at 142M strings/sec (~15.5 cycles/string) Should be possible to go faster using a char[256] mapping table and another char[256] specifying which chars end a cycle. The map data structure is an array of nodes linked into circular lists. #include <iostream> #include <algorithm> using namespace std; enum { alphabet_size = UCHAR_MAX+1 }; struct MapNode { MapNode *next; char c; bool last; MapNode() : next( this ), c(0), last(false) {} }; void CreateMap( string s, MapNode (&m)[ alphabet_size ] ) { MapNode *mprev = 0; replace( s.begin(), s.end(), ' ', '\0' ); char *str = const_cast<char*>(s.c_str()), *str_end = str + s.size() + 1; for ( char *pen = str; pen != str_end; ++ pen ) { if ( mprev == 0 ) sort( pen, pen + strlen( pen ) ); if ( * pen == 0 ) { if ( mprev ) mprev->last = true; mprev = 0; continue; } MapNode &mnode = m[ * pen ]; if ( mprev ) swap( mprev->next, mnode.next ); // link node in mnode.c = * pen; // tell it what char it is mprev = &mnode; } // make it easier to tell that a node isn't in any map for ( MapNode *mptr = m; mptr != m + alphabet_size; ++ mptr ) { if ( mptr->next == mptr ) mptr->next = 0; } } bool NextMapping( string &s, MapNode (&m)[ alphabet_size ] ) { for ( string::iterator it = s.begin(); it != s.end(); ++ it ) { MapNode &mnode = m[ * it ]; if ( mnode.next ) { * it = mnode.next->c; if ( ! mnode.last ) return true; } } return false; } int main( int argc, char **argv ) { MapNode m[ alphabet_size ]; CreateMap( argv[1], m ); string s = argv[2]; do { cerr << s << endl; } while ( NextMapping( s, m ) ); return 0; }
The way I would go about this is to create an array of indexes the same length as the string, all initialized at zero. We then treat this array of indexes as a counter to enumerate all the possible mappings of our source string. A 0 index maps that position in the string to the first mapping for that character, a 1 to the second, etc. We can step through them in order by just incrementing the last index in the array, carrying over to the next position when we reach the maximum number of mappings for that position. To use your example, we have the mappings 'a' => 'e', 'o' 'b' => 'i' With the input string "abba", we need a four element array for our indexes: [0,0,0,0] => "abba" [0,0,0,1] => "abbe" [0,0,0,2] => "abbo" [0,0,1,0] => "abia" [0,0,1,1] => "abie" [0,0,1,2] => "abio" [0,1,0,0] => "aiba" [0,1,0,1] => "aibe" [0,1,0,2] => "aibo" [0,1,1,0] => "aiia" [0,1,1,1] => "aiie" [0,1,1,2] => "aiio" [1,0,0,0] => "ebba" [1,0,0,1] => "ebbe" [1,0,0,2] => "ebbo" [1,0,1,0] => "ebia" [1,0,1,1] => "ebie" [1,0,1,2] => "ebio" [1,1,0,0] => "eiba" [1,1,0,1] => "eibe" [1,1,0,2] => "eibo" [1,1,1,0] => "eiia" [1,1,1,1] => "eiie" [1,1,1,2] => "eiio" [2,0,0,0] => "obba" [2,0,0,1] => "obbe" [2,0,0,2] => "obbo" [2,0,1,0] => "obia" [2,0,1,1] => "obie" [2,0,1,2] => "obio" [2,1,0,0] => "oiba" [2,1,0,1] => "oibe" [2,1,0,2] => "oibo" [2,1,1,0] => "oiia" [2,1,1,1] => "oiie" [2,1,1,2] => "oiio" Before we start generating these strings, we're going to need somewhere to store them, which in C, means that we're going to have to allocate memory. Fortunately, we know the length of these strings already, and we can figure out the number of strings we're going to generate - it's just the product of the number of mappings for each position. While you can return them in an array, I prefer to use a callback to return them as I find them. #include <string.h> #include <stdlib.h> int each_combination( char const * source, char const * mappings[256], int (*callback)(char const *, void *), void * thunk ) { if (mappings == NULL || source == NULL || callback == NULL ) { return -1; } else { size_t i; int rv; size_t num_mappings[256] = {0}; size_t const source_len = strlen(source); size_t * const counter = calloc( source_len, sizeof(size_t) ); char * const scratch = strdup( source ); if ( scratch == NULL || counter == NULL ) { rv = -1; goto done; } /* cache the number of mappings for each char */ for (i = 0; i < 256; i++) num_mappings[i] = 1 + (mappings[i] ? strlen(mappings[i]) : 0); /* pass each combination to the callback */ do { rv = callback(scratch, thunk); if (rv != 0) goto done; /* increment the counter */ for (i = 0; i < source_len; i++) { counter[i]++; if (counter[i] == num_mappings[(unsigned char) source[i]]) { /* carry to the next position */ counter[i] = 0; scratch[i] = source[i]; continue; } /* use the next mapping for this character */ scratch[i] = mappings[(unsigned char) source[i]][counter[i]-1]; break; } } while(i < source_len); done: if (scratch) free(scratch); if (counter) free(counter); return rv; } } #include <stdio.h> int print_each( char const * s, void * name) { printf("%s:%s\n", (char const *) name, s); return 0; } int main(int argc, char ** argv) { char const * mappings[256] = { NULL }; mappings[(unsigned char) 'a'] = "eo"; mappings[(unsigned char) 'b'] = "i"; each_combination( "abba", mappings, print_each, (void *) "abba"); each_combination( "baobab", mappings, print_each, (void *) "baobab"); return 0; }
You essentially want to do a depth-first search (DFS) or any other traversal down a directed acyclic word graph (DAWG). I will post some code shortly.
There is a link to the snippets archive which does that, here, Permute2.c. There is another variant of the string permutation (I guess you could then filter out those that are not in the map!) See here on the 'snippets' archive... Hope this helps, Best regards, Tom.
simple, recursive permute, with using char map[256] char *map [256]; /* permute the ith char in s */ perm (char *s, int i) { if (!s) return; /* terminating condition */ if (s[i] == '\0') { /* add "s" to a string array if we want to store the permutations */ printf("%s\n", s); return; } char c = s[i]; char *m = map [c]; // printf ("permuting at [%c]: %s\n", c, m); int j=0; /* do for the first char, then use map chars */ do { perm (s, i+1); s[i] = m[j]; } while (m[j++] != '\0'); /* restore original char here, used for mapping */ s[i] = c; return; } int main () { /* map table initialization */ map['a'] = "eo\0"; map['b'] = "i\0"; map['d'] = "gh\0"; /* need modifyable sp, as we change chars in position, sp="abba" will not work! */ char *sp = malloc (10); strncpy (sp, "abba\0", 5); perm (sp, 0); return 0; }