先看题
给定一个编码字符串 S。为了找出解码字符串并将其写入磁带,从编码字符串中每次读取一个字符,并采取以下步骤:
如果所读的字符是字母,则将该字母写在磁带上。
如果所读的字符是数字(例如 d),则整个当前磁带总共会被重复写 d-1 次。
现在,对于给定的编码字符串 S 和索引 K,查找并返回解码字符串中的第 K 个字母。
示例 1:
输入:S = “leet2code3”, K = 10
输出:“o”
解释:
解码后的字符串为 “leetleetcodeleetleetcodeleetleetcode”。
字符串中的第 10 个字母是 “o”。
示例 2:
输入:S = “ha22”, K = 5
输出:“h”
解释:
解码后的字符串为 “hahahaha”。第 5 个字母是 “h”。
示例 3:
输入:S = “a2345678999999999999999”, K = 1
输出:“a”
解释:
解码后的字符串为 “a” 重复 8301530446056247680 次。第 1 个字母是 “a”。
提示:
1、2 <= S.length <= 100
2、S 只包含小写字母与数字 2 到 9 。
3、S 以字母开头。
4、1 <= K <= 10^9
5、解码后的字符串保证少于 2^63 个字母。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/decoded-string-at-index
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
假装分析一下
1、认认真真解码字符串,最长有263 个字符,1GB=230B,外部排序都不一定够空间;
2、K明显远小于解码后的字符串(109 < 263),不信就打开计算器;
3、字符串只包含小写字母和2-9的数字,可以对大小进行压缩,但暂时来看没有太大意义;
4、2 <= S.length <= 100,编码字符串本身并不长;
5、字符出现的规律,例如"abc3de4fg2"。
按数字展开后分别是是:
i=3:“abcabcabc”;
i=3*3+2:“abcabcabcde abcabcabcde abcabcabcde abcabcabcde”;
i=(3*3+2)*4+2:“abcabcabcdeabcabcabcdeabcabcabcdeabcabcabcdefg abcabcabcdeabcabcabcdeabcabcabcdeabcabcabcdefg”
abc共3个字符,*3后有9个字符;
从1开始算,各字母的id为 n+3*j(n为第一次出现的id,0<=j<=2):
a:1,4,7
b:2,5,8
c:3,6,9
abcabcabcde共11各字符,*4后:
a:1,4,7、12,15,18、23,26,29、…
b:2,5,8、13,16,19、24,27,30、…
c:3,6,9、14,17,20、25,28,31、…
d:10,21,32,…
e:11,22,33,…
各字母的id为:n+11*j,0<=j<=3(此时当作有三个a,分别为1、4、7);
编码字符串每遇到一个数字都可以用通项展开,下一次遇到数字要展开时用到了上一次的通项。
对于数字3展开的通项:
0 <= i1 <=2
a1 = 1 + 3 * i1
b1 = 2 + 3 * i1
b1 = 3 + 3 * i1
对于数字4展开的通项:
0 <= i2 <= 3
a2 = a1 + 11 * i2
b2 = a1 + 11 * i2
b2 = a1 + 11 * i2
我们可以得到每一次展开后当前字符串的长度以及每个字符所在的位置、基于此,不需要直接展开字符串就可以找到K对应的字符了。
瞎操作一波
假装分析了一波,好像已经胸有成竹了。
但是。。
代码咋写啊?
先求总长度吧
这是解码到每个字符时的解码字符串长度:
再求下每个字符的标号
看输出跟上面的分析是不是很像?
更直接一点:
测试用例为:
"leet2code3"
10
解题思路的测试代码:
/*
d_id为数字的个数;
c_diff为数字解码前的长度;
c_len为数字解码后的长度;
cc_id为解码前的字符总个数;
c为解码前的字符"leetcode"
*/
for(i = 0; i < d_id; i++)
printf("%d\t%d\n", c_len[i], c_diff[i]);
for(i = 0; i < cc_id; i++)
printf("%c ", c[i]);
printf("\n");
printf("\n");
for(i = 0; i < cur_len; i++){
id = i;
tmp = 0;
for(j = d_id - 1; j > -1; j--){
if(c_len[j] <= id){
tmp += id - c_len[j] + 1;
id = c_diff[j] - 1;
//printf("tmp = %d\n", tmp);
}
if(c_diff[j] <= id){
id %= c_diff[j];
//printf(" %% %d\n", c_diff[j]);
}
}
printf("%d\t%c\t%d\n", i, c[id+tmp], id+tmp);
}
printf("\n");
打印为:
8 4
36 12
l e e t c o d e
0 l 0
1 e 1
2 e 2
3 t 3
4 l 0
5 e 1
6 e 2
7 t 3
8 c 4
9 o 5
10 d 6
11 e 7
12 l 0
13 e 1
14 e 2
15 t 3
16 l 0
17 e 1
18 e 2
19 t 3
20 c 4
21 o 5
22 d 6
23 e 7
24 l 0
25 e 1
26 e 2
27 t 3
28 l 0
29 e 1
30 e 2
31 t 3
32 c 4
33 o 5
34 d 6
35 e 7
哪个id对应哪个字符都已经安排的明明白白了。
骚起来
先看结果,提交的人不多,数据也不够多:
代码,也不太想整理了,就这样吧:
char * decodeAtIndex(char * S, int K){
long long id = 0, cur_len = 0, i, j, c_diff[101], c_len[101], c_id[101], c_num[101], tmp, cc_id = 0, d_id = 0;
char c[101], *ret;
ret = (char *)malloc(2);
ret[1] = '\0';
while(S[id] != '\0'){
if(S[id] < 0x40 && S[id] > '1'){
//数字的大小
tmp = S[id] - '0';
c_num[d_id] = tmp;
//数字解码后的长度
c_diff[d_id] = cur_len;
cur_len *= tmp;
c_len[d_id++] = cur_len;
c_diff[d_id-1]);
//解码后包含K?
if(cur_len >= K ){
break;
}
}else{
cur_len++;
//解码后但不重复的字符
c[cc_id] = S[id];
//解码字符对应的索引
c_id[cc_id++] = cur_len;
if(cur_len >= K ){
break;
}
}
id++;
}
i = K - 1;{//for(i = 0; i < cur_len; i++){
id = i;
tmp = 0;
for(j = d_id - 1; j > -1; j--){
if(c_len[j] <= id){
tmp += id - c_len[j] + 1;
id = c_diff[j] - 1;
//printf("tmp = %d\n", tmp);
}
if(c_diff[j] <= id){
id %= c_diff[j];
//printf(" %% %d\n", c_diff[j]);
}
}
//printf("%d\t%c\t%d\n", i, c[id+tmp], id+tmp);
}
//printf("\n");
*ret = c[id+tmp];
return ret;
}
看下别人的代码
别人的代码看起来就是简洁,函数名基本能看出来是干啥用的。
1、得出解码前后的字符串总长度;
2、从后往前根据size找K;
3、遇到数字后size%这个数字;