/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
char *** groupAnagrams(char ** strs, int strsSize, int* returnSize, int** returnColumnSizes){
// 存储各个字符串的归类情况以及同类别的下一个字符串的下标,初始化为0,因为初始就从下标为0的字符串开始遍历,无需从position得知,故初始化为0不影响结果。特殊情况数值下面会讲到。
int position[10000] = {0};
// 存储各个类别下字符串数目,以动态分配空间时使用
int word_num[10000] = {0};
// 存储字符串各个字母的数目
int words[26] = {0};
// 存储字符串各个字母数目的临时数组
int temp[26] = {0};
// 当前类别数目,动态变化
int num = 0;
// 开始遍历所有字符串,寻找字母异位词并分类
for (int i = 0; i < strsSize; i++) {
// position[i]为0说明该字符串还没有归类处理
if (position[i] == 0) {
// 重置words数组
for (int j = 0; j < 26; j++) {
words[j] = 0;
}
int length = strlen(strs[i]);
// 对字符串各字母计数
for (int j = 0; j < length; j++) {
int pos = strs[i][j] - 'a';
words[pos]++;
}
// first记录不与position[i]同类的首个字符串下标,遍历完一遍strs后下一轮遍历将直接从first开始
// left记录当前检测到的同类字符串的上一个字符串下标,实时更新,初始化为i。如一开始从第0个字符开始,若检测到下标为2的字符串为第一个与第0个字符串字母异位的字符串,则position[left],也就是position[0]置为2,left更新为2,然后继续往strs右边推进。
int first = -1, left = i;
// num1记录当前种类下的字符串数目,初始化为1因为此时已经至少有一个字符串在这一类中(即i)。
int num1 = 1;
// 开始遍历检查字符串是否归属于当前类别
for (int j = i + 1; j < strsSize; j++) {
// 仅对还没有归类的字符串进行检查
if (position[j] == 0) {
int length1 = strlen(strs[j]);
if (length != length1) {
// 字符串长度都不同,显然不属于同一类别,此时若不属于同一类别的首个字符串下标变量还没有更新,则在这里更新,下同。
if (first < 0) {
first = j;
}
continue;
}
// 复刻当前类别字符串字母数目的信息到临时数组
for (int k = 0; k < 26; k++) {
temp[k] = words[k];
}
// 当前字符串是否归属于当前类别的标志,1为真,初始化为1
int flag = 1;
// 检查
for (int k = 0; k < length1; k++) {
int pos = strs[j][k] - 'a';
temp[pos]--;
// 检查到某字母数目超出,表明该字符串不属于当前类别
if (temp[pos] < 0) {
if (first < 0) {
first = j;
}
flag = 0;
break;
}
}
// flag没有置0,当前字符串满足条件,更新当前类别字符串数目,此时该类别中上一个字符串的下标为left(上面说过),更新position[left]为j,这样到时候从小标为i的字符串寻找下一个该类别下的字符串下标时可直接从position[i]获取。
//更新left为最新的j
if (flag) {
num1++;
position[left] = j;
left = j;
}
}
}
// 此时已经退出循环,即该类别的字符串已经全部获取,若first仍然小于零,表明strs中已经没有还没归类的字符串,则可以直接将position[left]置为10001,一个肯定大于strs长度的值表示分类结束。
if (first < 0) {
position[left] = 10001;
}
// first大于等于零(实际上肯定大于0),表示strs中仍然有没有归类的字符串,且这些字符串中下标最小的为first,则可以将position[left]置为first,这样的话再将上一类别的字符串分类写入到ans数组中后可以直接将下标定位到first处开始下一类别的字符串写入答案
else {
position[left] = first;
i = first - 1;
}
// 更新存储每一类别下字符串数目的数组,同时类别数num加一
word_num[num++] = num1;
}
}
*returnSize = num;
*returnColumnSizes = (int*)malloc(sizeof(int) * (*returnSize));
char*** ans = (char***)malloc(sizeof(char**) * num);
// 初始化init为0,表示答案从第0个字符串开始写入
int init = 0;
for (int i = 0; i < num; i++) {
(*returnColumnSizes)[i] = word_num[i];
ans[i] = (char**)malloc(sizeof(char*) * word_num[i]);
int length2 = strlen(strs[init]) + 1;
for (int j = 0; j < word_num[i]; j++) {
ans[i][j] = (char*)malloc(sizeof(char) * length2);
for (int k = 0; k < length2 - 1; k++) {
ans[i][j][k] = strs[init][k];
}
ans[i][j][length2 - 1] = '\0';
// 更新下一个要写入ans的字符串的下标
init = position[init];
}
}
return ans;
}
代码有点复杂,时间复杂度也比较高,但还是可以过的。