LeetCode.17 九键字母组合

题目含义

对于这样的一个按键。如果按下一串数字,会出现什么样的字母组合。

Input: "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

输出的时候,不需要考虑顺序。

思路

总的思想还是比较简单的。首先根据每个数字上面的字母数,得到结果总数。

int len_rec[] = {
['2'] = 3,
['3'] = 3,
['4'] = 3,
['5'] = 3,
['6'] = 3,
['7'] = 4,
['8'] = 3,
['9'] = 4,
};
/*计算总的结果数*/
*returnSize = 1;
char *p = digits;
while (*p) {
(*returnSize) *= len_rec[*p] > 0 ? len_rec[*p] : 1;
p++;
}

得到总的结果数之后,可以一次性地把所有需要动态申请的内存都申请完。

/*申请结果空间*/
size_t retSize = sizeof(char*) * (*returnSize + 1);
char **ret = (char**)malloc(retSize);
memset(ret, 0, retSize);
for (size_t i = 0; i < *returnSize; i++) {
size_t n = sizeof(char) * (d_len + 1);
ret[i] = (char*)malloc(n); // ERROR: char *buf -> ret[i]
memset(ret[i], 0, n);
}

初始处理,比如给定的数字串是”999”。

1. 第一个数字为9
生成相应的字符串
ar1 =
[
"w",
"x",
"y",
"z",
]
2. 第二个数字为9
ar2 = []
for c in "wxyz":
新数组 = ar1 + c;
ar2 = [ar2; 新数组]
如果把循环展开,就是
c = "w"
新数组 = ["ww", "xw", "yw", "zw"]
ar2 = ["ww", "xw", "yw", "zw"]
第二次循环
c = "x"
新数组 = ["wx", "xx", "yx", "zx"]
ar2 = [
"ww", "xw", "yw", "zw",
"wx", "xx", "yx", "zx",
]
...
假设ar1长度为N。
那么后面每次循环的时候,只需要把
这N个字符串拷贝到新的位置。
a. 第一次添加的是0~N-1
b. 第二次处理的字符串是N~2N-1
c. 第三次处理的是2N~3N-1
3. 递归处理第三个数字

这段代码的关键就是在:

size_t i = 0;
// pos指向当前需要处理的是哪个数字
// 同时也是结果字符串需要填充的字符
// 串的位置。
/*
* 这段代码利用9->"wxyz"中的"w"来生成
* ar1,并且得到长度len(ar1);
*/
size_t pos = str - a;
while (ret[i][0]) { // ERROR: ret[i] -> ret[i][0]
ret[i][pos] = *dic;
assert(0 <= i && i < retLen);
assert(0 <= pos && pos < dstrLen);
i++;
}
/*
* curlen就是指len(ar1);
*/
const size_t curlen = i;
// *p指向9->wxyz中的第二个字母x
char *p = dic + 1;
// 从第二个字母开始依次处理每个字母
while (*p) {
size_t npos = (p-dic)*curlen;
// 在处理的时候,把
// [0, len(ar1))拷贝到
// [i*len(ar1), (i+1)*len(ar1) )
// 然后再修改pos位置的字母
for (size_t i = 0; i < curlen; i++) {
// 注意处理的字符串
strcpy(ret[npos + i], ret[i]);
assert(0 <= (npos+i) && (npos+i) < retLen);
assert(0 <= pos && pos < dstrLen);
ret[npos+i][pos] = *p;
}
p++;
}

这里并没有使用任何第三方库。直接使用一下递归就可以了。

完整的代码

/*单字符串快速查找*/
char* str_rec[] = {
['2'] = "abc",
['3'] = "def",
['4'] = "ghi",
['5'] = "jkl",
['6'] = "mno",
['7'] = "pqrs",
['8'] = "tuv",
['9'] = "wxyz",
};
int len_rec[] = {
['2'] = 3,
['3'] = 3,
['4'] = 3,
['5'] = 3,
['6'] = 3,
['7'] = 4,
['8'] = 3,
['9'] = 4,
};
/*
* 这里递归进行处理
* str指向当前要处理的字符指针
* ret已经申请好所有的空间。
*/
void append_char(const char *a, char *str, char **ret) {
/*已经结束*/
if (!str || !(*str)) {
return;
}
/*当前字符映射过去的字符串*/
char *dic = str_rec[*str];
if (!dic || !(*dic)) return;
/*处理dic里面的第一个字符*/
size_t i = 0;
size_t pos = str - a;
while (ret[i][0]) { // ERROR: ret[i] -> ret[i][0]
ret[i][pos] = *dic;
i++;
}
/*当前ret的长度*/
const size_t curlen = i;
char *p = dic + 1;
while (*p) {
size_t npos = (p-dic)*curlen;
for (size_t i = 0; i < curlen; i++) {
strcpy(ret[npos + i], ret[i]);
ret[npos+i][pos] = *p;
}
p++;
}
append_char(a, str + 1, ret);
}
/**
* Return an array of size *returnSize.
* Note: The returned array must be malloced, assume caller calls free().
*/
char** letterCombinations(char* digits, int* returnSize) {
/*如果为空串*/
if (NULL == digits || *digits == 0) {
*returnSize = 0;
return NULL;
}
/*计算总的结果数*/
*returnSize = 1;
char *p = digits;
while (*p) {
(*returnSize) *= len_rec[*p] > 0 ? len_rec[*p] : 1;
p++;
}
/* d_len表示当前数字串的长度,
* 结果字符串的长度是d_len + 1
*/
int d_len = p - digits;
/*申请结果空间*/
size_t retSize = sizeof(char*) * (*returnSize + 1);
char **ret = (char**)malloc(retSize);
memset(ret, 0, retSize);
for (size_t i = 0; i < *returnSize; i++) {
size_t n = sizeof(char) * (d_len + 1); // 这里要多申请一个char
ret[i] = (char*)malloc(n); // ERROR: char *buf -> ret[i]
memset(ret[i], 0, n); // ERROR: 这里一定要把n长度的字符串清0,而不只是d_len
}
/*处理第一个字符*/
char *str = str_rec[*digits];
for (size_t i = 0; str[i]; i++) {
ret[i][0] = str[i];
}
append_char(digits, digits + 1, ret);
return ret;
}

错误处理

在提交的时候,总是遇到

sysmalloc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >= (unsigned long)((((__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 *(sizeof(size_t))) - 1)) & ~((2 *(sizeof(size_t))) - 1))) && ((old_top)->size & 0x1) && ((unsigned long) old_end & pagemask) == 0)' failed.

说的是内存访问越界。

// 这里在申请内存的时候,要多一个char
// 并且要把申请的内存空间请0.
// memset(ret[i], 0, d_len);会导致上面那个错.
for (size_t i = 0; i < *returnSize; i++) {
size_t n = sizeof(char) * (d_len + 1); // ERROR: 这里要多申请一个char!!!
ret[i] = (char*)malloc(n); // ERROR: char *buf -> ret[i]
memset(ret[i], 0, n); // ERROR: 这里一定要把n长度的字符串清0,而不只是d_len
}