给定一个英语字典,找出其中的所有变位词集合。例如,“pots”,“stop”和“tops”互为变位词,因为每一个单词都可以通过改变其他单词中的字母顺序来得到。
首先想到的方法应该是使用一个两层循环,遍历字典,然后再比较每个单词是否是变位词,在比较单词的时候,可以先对每个单词的所有字母进行排序,然后再比较,总的时间复杂度为O(m*logm*n2),也可以使用一个大小为26的字符数组做Hash来进行对比。这样的算法总的时间复杂度为O(m*n2)。对字典使用一个两层循环遍历,可见效率并不高。所以需要再考虑其他算法。
更好些的方法是,我们可以考虑为每个单词增加一个标识,然后再以标识对单词进行排序,这样排序后,相同标识的单词就分在一起,这样就找出了所有单词的变位词集合。可以将该方法分为以下三个步骤:
struct { char identity[MAX_SIZE]; char word[MAX_SIZE]; }所有的单词经过步骤1后就得到了一个包含标识和单词的数组,这样使用快速排序对这个数组按照标识大小排序。
#define MAX_SIZE 100 #include<stdio.h> #include<string.h> #include<stdlib.h> #include<ctype.h> void str_to_lower(char *str); void sign(char words[][MAX_SIZE], int length, char sig[][MAX_SIZE]); int char_compare(const void *c1, const void *c2); int str_compare(const void *s1, const void *s2); int partition(char words[][MAX_SIZE], char sig[][MAX_SIZE], int left, int right); void qsort_str(char words[][MAX_SIZE], char sig[][MAX_SIZE], int left, int right); void sort(char words[][MAX_SIZE], char sig[][MAX_SIZE], int length); /** * 比较两个字符的大小 */ int char_compare(const void *c1, const void *c2){ return *(char *)c1 - *(char *)c2; } /** * 所有的单词都存在words数组中,为words单词中的所有单词求他的标识, * 其中标识按照单词中的字母顺序排列的字母序列 */ void sign(char words[][MAX_SIZE], int length, char sig[][MAX_SIZE]) { int i; for(i = 0; i < length; i++) { strcpy(sig[i], words[i]); qsort(sig[i], strlen(sig[i]), sizeof(char), char_compare); } } void str_to_lower(char *str) { int i; int len = strlen(str); for(i = 0; i < len; i++) { str[i] = tolower(str[i]); } } /** * 比较两个字符串的大小,封装之后可以用qsort函数排序字符串 */ int str_compare(const void *s1, const void *s2) { return strcmp((char *)s1, (char *)s2); } int partition(char words[][MAX_SIZE], char sig[][MAX_SIZE], int left, int right) { char temp_word[MAX_SIZE]; char temp_sig[MAX_SIZE]; strcpy(temp_sig, sig[left]); strcpy(temp_word, words[left]); while(left < right) { while(str_compare(temp_sig, sig[right]) < 0 && left < right) right--; strcpy(sig[left], sig[right]); strcpy(words[left], words[right]); while(str_compare(temp_sig, sig[left]) >= 0 && left < right) left++; strcpy(sig[right], sig[left]); strcpy(words[right], words[left]); } strcpy(words[right], temp_word); strcpy(sig[right], temp_sig); return right; } void qsort_str(char words[][MAX_SIZE], char sig[][MAX_SIZE], int left, int right) { int part; char temp[MAX_SIZE]; if(left >= right) return; part = partition(words, sig, left, right); qsort_str(words, sig, left, part - 1); qsort_str(words, sig, part + 1, right); } /** * 使用快速排序对字符串数组进行排序 */ void sort(char words[][MAX_SIZE], char sig[][MAX_SIZE], int length) { qsort_str(words, sig, 0, length - 1); } /*汇总变位词*/ void squash(char words[][MAX_SIZE], char sig[][MAX_SIZE], int length) { int i; char oldsig[MAX_SIZE]; strcpy(oldsig, sig[0]); printf("%-6s:", oldsig); for(i = 0; i < length; i++) { if(strcmp(oldsig, sig[i]) != 0) { strcpy(oldsig, sig[i]); printf("\n"); printf("%-6s:", oldsig); } printf("%-6s", words[i]); } printf("\n"); } void print(char words[][MAX_SIZE], char sig[][MAX_SIZE], int length) { int i; for(i = 0; i < length; i++) { printf("%s %s\n", sig[i], words[i]); } } int main() { char words[][100] = {"pans", "pots", "opt", "snap", "stop", "tops"}; char sig[6][100]; int i; sign(words, 6, sig);//为单词添加标识 print(words, sig, 6); sort(words, sig, 6);//使用标识排序 printf("****************************************\n"); print(words, sig, 6); printf("----------------------------------------\n"); squash(words, sig, 6);//汇总变位词 return 0; }以上实现是以6个单词为例来实现,如果字典中单词较多,内存容不下时,可以对上述的3个步骤使用文件来保存临时结果。