UVa12333
给定一个斐波那契数列的前缀(小于等于40
),找出具有该前缀的斐波那契数的最小下标(小于100000
)。
这道题输入的前缀长度可以达到40
,下标的上界为100000
,而斐波那契数列的增长速度很快,因此绝对会超出内置类型所能表示的范围,所以必须要使用高精度算法。
根据斐波那契数列的递推公式,大概到2000
项时,斐波那契数就有约100
位了,简单估计一下,100000
项的斐波那契数的长度有几万位,这肯定会超时,所以还需要进行优化。
由于输入前缀长度最大只有40
位,因此只需要保证前40
位的正确性即可,所以可以在计算的过程中只对最高的40
位有效数字进行计算。但是为了保证前40
位的正确性,应该多保留一些位数。
如果高精度算法使用10^18
次方进制,那么一位数字便可以表达十进制下的18
个有效数字,只需保留大概4
个数字即可满足题目要求,精度范围从55
位到72
位。
最后,前缀查找应该使用字典树(Trie)。由于题目要求输出具有该前缀的斐波那契数的最小的下标,因此在向字典树中添加前缀时,每到一个节点都应该将index
更新为较小的值。
#include <iostream>
#include <algorithm>
#include <array>
#include <string>
#include <vector>
#define BASE 1000000000000000000
using namespace std;
struct TrieNode
{
int index;
array<TrieNode*, 10> next;
TrieNode() : index(-1)
{
next.fill(nullptr);
}
void add(const string &s, int value)
{
TrieNode* ptr = this;
for (char c : s)
{
if (ptr->next[c - '0'] == nullptr) {
ptr->next[c - '0'] = new TrieNode();
}
ptr = ptr->next[c - '0'];
if (ptr->index == -1) {
ptr->index = value;
}
else {
ptr->index = min(ptr->index, value);
}
}
}
int search(const string &prefix)
{
TrieNode* ptr = this;
for (char c : prefix)
{
if (ptr->next[c - '0'] == nullptr) {
return -1;
}
ptr = ptr->next[c - '0'];
}
return ptr->index;
}
};
string Convert2String(const vector<long long> &fib)
{
string s, tmp;
s.append(to_string(fib.back()));
for (auto riter = fib.rbegin() + 1; riter != fib.rend(); riter++)
{
tmp = to_string(*riter);
s.append(18 - tmp.length(), '0');
s.append(tmp);
}
return s;
}
vector<long long> PrecomputeFib(const vector<long long> &fn_2, vector<long long> &fn_1)
{
long long carry = 0;
size_t i = 0, j = 0;
vector<long long> result;
while (i < fn_2.size() && j < fn_1.size()) {
result.push_back(fn_2[i] + fn_1[j] + carry);
carry = result.back() / BASE;
result.back() %= BASE;
i++, j++;
}
while (j < fn_1.size()) {
result.push_back(fn_1[j] + carry);
carry = result.back() / BASE;
result.back() %= BASE;
j++;
}
if (carry != 0) {
result.push_back(carry);
}
if (result.size() > 4) {
fn_1.assign(fn_1.begin() + (result.size() - 4), fn_1.end());
result.assign(result.begin() + result.size() - 4, result.end());
}
return result;
}
void PrecomputeFibPrefix(TrieNode &root)
{
vector<long long> fn_2(1, 1), fn_1(1, 1), fn;
int index = 2;
root.add("1", 0);
while (index < 100000) {
fn = PrecomputeFib(fn_2, fn_1);
root.add(Convert2String(fn), index);
fn_2 = fn_1;
fn_1 = fn;
index++;
}
}
int main()
{
int n = 0;
cin >> n;
TrieNode root;
PrecomputeFibPrefix(root);
for (int i = 0; i < n; i++)
{
string prefix;
cin >> prefix;
cout << "Case #" << i + 1 << ": " << root.search(prefix) << endl;
}
return 0;
}
/*
15
1
12
123
1234
12345
9
98
987
9876
98765
89
32
51075176167176176176
347746739
5610
*/