思路
题目地址:Vijos P1005 超长数字串
想法一
最直接的做法,是维护一个字符串,枚举这个无限长的S字符串。然后直接在S中寻找第一次出现位置。空间是有限的,所以我们不可能无限制枚举。可以根据内存限制和测试点范围,估计一个大概的长度,然后填满这个有限长度的S。依照这个想法快速写出代码,可以:
- 能提交一个部分AC的程序,捞到保底分
- 方便构造复杂一点的测试用例
想法二
上面这样当然没法得到全部的正确答案。这个题目很明显,A的长度不超过200,这个长度为 200 的数,假设为 N,一定会出现在 S 中。知道某个 N,我们是可以根据规律直接计算出他出现的位置。所以这个问题就转化成求最小的 N,最小的 N 是可以通过枚举 N 的位数来直接尝试出的。
按照这个思路,可以得到求解方式为:
- 从 1 到 len(A) 枚举 N 的长度 L
- 对于每个长度 L,在 A 中从后往前构造长度为 L 的数 M,检测 M 构造的数是否满足 A
- 对于每个长度 L,不停的用 M 更新 N,求出最小的 N 值,记录 N 值在字符串 A 的偏移量
- 根据 N 和偏移量计算位置
- S 从 1 开始编号,所以需要位置 +1 得到答案
不考虑数据大小,这个方式是正确的思路,稍微注意边界条件,可以得到正确的结果。边界条件包括:
- 字符串 A 全为 0,直接在前面补 1 得到 N,然后使用偏移量 -1 进行计算
- 每一个构造的 M 不能以 0 开头,如果是,直接跳过
- 在 A 中构造 L 长度的数值 M。如果 M 的位数不够,需要从前面的内容填充
- 如果前面 (L - 缺失位数)的内容全为 9,M 需要填充(L - 缺失位数)的 0
- 否则填充 (L - 缺失位数) 长度的 A中当前枚举位置前(L - 缺失位数)的值加 1
用一个例子来验证思路,假如输入数据是 0000
。
- 直接补 1,得到最小的 N 为 10000。
10000
是5位数,L=5。计算1-4位的值,再加上第5位的值,减去 N 在 A 的偏移量 -1,就是位置。- 位置 +1 得到答案
每个长度为 L 的数在 S 中消耗的位数为 L * 9 * 10^(L-1)
,可以得出
位数 | 计算方式 | 占的长度 |
---|---|---|
1 | 1 * 9 * 10^(1-1) | 9 |
2 | 2 * 9 * 10^(2-1) | 180 |
3 | 3 * 9 * 10^(3-1) | 2700 |
4 | 4 * 9 * 10^(4-1) | 36000 |
第五位的偏移量为 5*(10000-10000)=0,位置为 9+180+2700+36000+0-(-1)=38890,+1 得到答案 38891。
想法三
按照思路二,写成了新的代码。但是还不能通过全部的点。A 长度最多为 200,这个范围肯定是超过最大的整型了,需要高精度。
如果是以前竞赛的时候,时间不够了,估计用 Int64 整完想法二的代码,差不多这个题就这么提交了。但是,现在,是为了学习 Go 来做题的,Go 有处理大数据的 math/big!
将想法二的代码,涉及到计算的地方都使用现成的big.Int
相关的方法替换,出来的结果就是能支持 200 位 N 的结果计算的正确代码。
几个测试点
A | 结果 |
---|---|
21 | 15 |
00 | 191 |
99999 | 438886 |
9999999999 | 88888888881 |
0000000000 | 98888888891 |
代码
1 | /* |