- 斐波那契数列 : 1,1,2,3,5,8 ......,F(n) = F(n-1) + F(n-2) ,F(0)=F(1)=1
- 任务:给出一个斐波那契数的前几个数字(不超过40个数字),找出以它开头的最小的Fibonacci数的序号。如果序号小于100000的Fibonacci数均不符合条件,输出-1.
- 坑点:卡时间,首先是10万个数必须都要求出来,才能判断是不是有符合的,然后这题的数据规模是 小于等于 50000个数,每算一个遍历一次是不行的,肯定超时,还有就是计算,完整精确计算是不能的,只能保留一定数量的有效数字。
- 自己想不出来,看了看别人的解法,发现都是构造字典树,字典树没学过。虽然看懂了大概,但是我不想用,因为他们都构造了一个400多万元素的数组,粗略估计了一下,大概200MB内存,感觉代价有点高。
- 反正开头肯定是打表,10万数组是少不了的。既然主要是查询费时间,那就从查询入手改造。思路是先打表,然后排序,再二分查找位置,然后从这个位置向下搜索符合条件的,从中选一个序号小的,遇到不符合条件的就终止。二分查找时间就很低了,拿自己电脑测试了一下,开头的打表排序也就0.7s左右。
- 通俗解释一下:因为字符串也能比较,就把他们类比成数字好了,例如 “aaa”“abc”“abc”“abcd”“c”“ccd”“d”“d”“d”“dff”就可以记作为 1, 5, 5, 6, 7, 8, 9, 12, 12, 12假设我想要搜索前缀为“ab”的,好比在右面寻找数字4,利用二分找到他的位置,“aaa”“ab”“abc”“abc”“abcd”“c”“ccd”“d”“d”“d”“dff”。1, 4, 5, 5, 6, 7, 8, 9, 12, 12, 12,只是找位置而已,并非插入数组中,他排在第0个元素后,所以位置pos=0。然后从位置pos向后搜索(不搜索位置pos)包含此前缀“ab”的,只要遇到一个不包含的,就停止,因为后面的肯定也不包含了,从这些包含前缀的选一个序号小的。如果上来第一个(pos+1)就停下了,肯定是-1的那种情况。
- 保存大数字,这个我写的比较丑陋,反正够用,用60位来保存有效数字,还要记录指数,因为指数不一样不好相加,还要移位
代码
#include <iostream>
#include <string>
#include <cstdio>
#include <algorithm>
#define maxn 60
using namespace std;
struct Bigint {
int bit[maxn] = {1}, index = 0;//由低到高
void indent(int maxnbit) {
for(int i = 0; i < maxn - 1; bit[i] = bit[i + 1], i++);
bit[maxn - 1] = maxnbit;
index++;
}
void operator+=(Bigint& b) {
if(index < b.index) indent(0);
else if(index > b.index) b.indent(0);
int c = 0;
for(int i = 0; i < maxn; i++) {
int tem = bit[i] + c + b.bit[i];
bit[i] = tem % 10;
c = tem / 10;
}
if(c == 1) indent(1);
}
};
struct Node {
int fid;
string s;
bool operator<(const Node& n2) {
return s < n2.s;
}
} arr[100001];//把arr[0]的string设置为最小
inline void getnode(const Bigint& b, Node& node) { //将前n个数字转为string,不够n个按实际算
static int fid = 0;
int pos = maxn - 1;
for(; !b.bit[pos]; pos--);//不要最高位的0
for(int i = pos, j = 0; i >= 0 && j < 40; i--, j++)
node.s += char(b.bit[i] + '0');
node.fid = fid++;
}
int possearch(int ibegin, int iend, const string& str) {
if(ibegin + 1 >= iend) return ibegin;
int mid = (ibegin + iend) / 2;
return str <= arr[mid].s ? possearch(ibegin, mid, str) : possearch(mid, iend, str) ;
}
int main() {
Bigint a, b;
for(int i = 1; i <= 100000; a += b, b += a) {
getnode(a, arr[i++]);
getnode(b, arr[i++]);
}
arr[0].s="000";//便于查找
sort(arr, arr + 100001);
int t, ncase = 1;
string num;
cin >> t;
while(t--) {
cin >> num;
int pos = possearch(0, 100000, num), n = num.size(), ans = -1;
for(int i = pos + 1; i < 100001; i++)
if(arr[i].s.substr(0, n) == num) {
if(ans == -1 || ans > arr[i].fid) ans = arr[i].fid;
} else break;
printf("Case #%d: %d\n", ncase++, ans);
}
}
2024.5.5更新,偶然又翻到2018年2月这道题,重写了一下,原理不变,增加可读性。
#include <iostream>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int maxn = 60;
using namespace std;
struct FibNum{
int leading[maxn] = {}; //前60位数字,不够60位的前面补 0
int exp = 0; //指数,一个FibNum数值约等于 leading * 10^exp,仅超过60位的大数需要
FibNum(int val = 0) { //初始化
exp = 0;
leading[maxn - 1] = val;
}
//特殊移位运算,右移n位,同时指数+n,注意:仅限右移
FibNum& shift(int n = 1){
for(int i = maxn - 1; i >= 0; i--){
leading[i] = (i-n >= 0 ? leading[i-n] : 0);
}
exp += n;
return *this;
}
//重载一下 += 运算符
FibNum& operator+=(const FibNum& b){
int i = maxn - 1, j = maxn - 1; // 指数对齐后的 leading 下标
if(this->exp >= b.exp){
j -= this->exp - b.exp;
}else{
this->shift(b.exp - this->exp);
}
int carry = 0; //进位
for(; i>=0; i--,j--){
int x = this->leading[i];
int y = (j >= 0) ? b.leading[j] : 0;
int t = x + y + carry;
this->leading[i] = t % 10;
carry = t / 10;
}
// 若最高位溢出,需要右移
if(carry > 0){
this->shift(1);
this->leading[0] = carry;
}
return *this;
}
};
const int max_nums_len = 41;
struct FibNode{
char nums[max_nums_len] = {};
int fib_id = -1;
bool operator<(const FibNode& b) const{
return strcmp(nums, b.nums) < 0;
}
void set_value(const FibNum& f, int fid = -1){
fib_id = fid;
int i = 0, j = 0;
for(; f.leading[i] == 0; i++); // 忽略前导0
for(; i<maxn && j<max_nums_len-1; i++){
nums[j++] = f.leading[i] + '0';
}
nums[j] = '\0';
}
};
const int max_cnt = 100000; //小坑:恰好是第 100000 个fib数时要输出-1
FibNode fib_arr[max_cnt];
void pre_table(){ //打表
int fid = 0;
FibNum a(1) , b(1);
while(fid < max_cnt){
fib_arr[fid].set_value(a, fid);
fid++;
if(fid >= max_cnt)
break;
fib_arr[fid].set_value(b, fid);
fid++;
a += b;
b += a;
}
sort(fib_arr, fib_arr + max_cnt);
}
int query(const FibNode& q){
int ret = -1;
int qlen = strlen(q.nums);
int first = lower_bound(fib_arr, fib_arr + max_cnt, q) - fib_arr;
for(int i = first; i < max_cnt; i++){
if(strncmp(fib_arr[i].nums, q.nums, qlen) == 0){
ret = ret == -1 ? fib_arr[i].fib_id : min(ret, fib_arr[i].fib_id);
}else{
break;
}
}
return ret;
}
int main() {
pre_table();
FibNode q;
int t, ncase = 1;
cin >> t;
while(t--) {
cin >> q.nums;
printf("Case #%d: %d\n", ncase++, query(q));
}
}