开篇
本篇文章的主题是使用C语言来解决变位词排序的问题,题目来源是《编程珠玑》第2章的第三个问题。文中代码实现是根据原文中的思路分析的一个个人尝试。后续,原文在拓展阅读中提到的Tom Cargill版本的代码,也许会在后面以单独的文章来介绍。
不过本文代码的实现思路,和Tom Cargill版非常相似,代码虽有不同,但思想是一致的,也可以作为参考。
问题概要
给定一个英语词典,找出其中的所有变位词集合。例如:“pots”“stop”“tops”互为变位词,因为每一个单词都可以通过改变其他单词中字母的顺序来得到。
思路分析
标识词典中的每一个词,使得在相同变位词类中的单词具有同样的标识。然后,将所有具有相同标识的单词集中在一起。这将原始的变位词问题简化为两个子问题:选择标识和集中具有相同标识的单词。
对第一个问题,即选择标识的问题,可以使用基于排序的标识:将单词中的字母按照字母表顺序排列。"deposit"的标识就是"deiopst",这也是"dopiest"和其他任何在该类中的单词的标识。
对第二个问题,即集中具有相同标识的单词的问题,我们将所有的单词按照其标识的顺序排序。关于该算法的最好的描述就是Tom Cargill的翻手表示:先用一种方式排序(水平翻手),再用另一种方式排序(垂直翻手)。
上面的思路分析,采摘自《编程珠玑》原文,通俗解释就是:对于变位词,虽然字母的顺序不一样,但使用的字母以及字母的个数都是一样的,比如说"stop"和"pots",那么,我们对这写词进行一个排序就好了,排序后均为:opst。也就是说,如果给这两个词添加标识的话,这两个词的标识均为"opst",如此,便可轻松识别出变位词了。
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 字符串比较函数,用于qsort
int cmpChar(const void* a, const void* b) {
return *(const char*)a - *(const char*)b;
}
// 将字符串按字典顺序排序
char* sortString(char* s) {
char* sorted = strdup(s); // 复制字符串,以便排序
qsort(sorted, strlen(sorted), sizeof(char), cmpChar);
return sorted;
}
// 结构体,用于存储单词和其排序后的标识
typedef struct {
char* word;
char* sortedWord;
} WordEntry;
// 比较排序后的标识
int cmpWordEntry(const void* a, const void* b) {
return strcmp(((WordEntry*)a)->sortedWord, ((WordEntry*)b)->sortedWord);
}
int main() {
char* dictionary[] = {"pots", "stop", "dopiest", "tops", "deposit", "pans", "snap", "opt"};
int n = sizeof(dictionary) / sizeof(dictionary[0]);
WordEntry entries[n];
int i;
// 对每个单词进行排序,并填充entries数组
for (i = 0; i < n; ++i) {
entries[i].word = dictionary[i];
entries[i].sortedWord = sortString(dictionary[i]);
}
// 根据排序后的单词进行排序
qsort(entries, n, sizeof(WordEntry), cmpWordEntry);
// 打印变位词集合
printf("变位词集合: \n");
for (i = 0; i < n;) {
int j = i;
// 查找具有相同的标识的词
while (j < n && strcmp(entries[i].sortedWord, entries[j].sortedWord) == 0) {
printf("%s ", entries[j].word);
++j;
}
printf("\n");
i = j;
}
// 释放分配的内存
for (i = 0; i < n; i++) {
free(entries[i].sortedWord);
}
return 0;
}
注
变位词的问题,至此还没有结束!后面Tom Cargill版本的代码,以及对于该问题的扩展,都会以单独的文章来解读。因为我个人对于技术博客的期望是:每天沉淀一个知识点,每天进步一点点。所以,每篇文章并不会特别长。
但愿今天的代码,能对正在阅读的你有些许参考意义。
感谢阅读。