UVA - 12333 Revenge of Fibonacci

/*
  参考:
  http://www.cnblogs.com/kunsoft/p/5312726.html
  
  收获:
  1. 了解了字典树,可参考几个博客:
  http://blog.csdn.net/u012469987/article/details/43874051
  http://blog.csdn.net/u011787119/article/details/46991691
  
  2.复习了常量指针和指向常量的指针
  http://blog.csdn.net/luoweifu/article/details/45600415
  
  3.复习了之前在看《挑战》时,只是略有了解的滚动数组
  http://blog.csdn.net/u012577123/article/details/39211361
  滚动数组最大的意义,在于缩减空间方面,对时间的影响不大
  
  说明:
  4.
  关于 if(e - s > 50)  ++s; 这句代码,一开始我一直不理解原因,也不明白为什么要取50,后来查阅了有关题解,才明白过来
  因为要求精确到前40位,为了避免进位的误差,所以我们计算的时候,肯定要比40算的多一些(否则,如果真的取40,可能会有一些进位误差没有考虑到,例如有某个F数组的元素,算出来有进位,但是没有进行进位处理,如果这个错误的位置,恰好在我们需要的前40的精度里,那么就会WA了)
  
  最后试验的结果时, e - s至少为49,当然,取200什么的也没问题,这个主要是代表了一个精度,让我们知道,我们求得的斐波拉契数列,大概有前多少位的范围,是精确可靠,可以相信的,所以往多了取当然没问题,但是不能取少了,所以最合适的是50、60的样子
  
  另附上其他博客里的一些解释
  
  4.1. 因为只是需要前40位,而且fibonacci数列上升速度很快, 所以我们保留60位的精度就足够了。
  http://www.cnblogs.com/Jadon97/p/6922619.html
  
  4.2. 因为只要前40位数字。所以在加的时候长度大于50左右就截掉个位上的,保留高位。
  (这是我觉得最清楚而言简意赅的解释了,因为斐波拉契数可能会有很多位,所以我们要截断,可是只截断40位,可能有未处理的进位导致WA,所以我们截断前50位左右)
  http://www.cnblogs.com/kuangbin/archive/2012/09/06/2673897.html
  
  4.3.我们可以把斐波那契数列精确到50多位,然后只存40位即可,这样就防止进位的误差。在斐波那契数列加法过程中,我们只把它的前50多位进行相加,不然存不下。
  http://blog.csdn.net/u012965373/article/details/40636371
  
*/



#include <bits/stdc++.h>
using namespace std;

struct Node
{
	int id;
	Node* next[10];
	Node()
	{
		id = -1;
		memset(next, 0, sizeof(next));
	}
};

char Fib[50], In[50];
int F[2][1024000];
Node *const root = new Node();

void add_node (char* str, int id)
{
	Node *u = root;
	for (int i = 0, len = (int)strlen(str); i < len && i <= 40; i++)
	{
		int v = str[i] - '0';
		if (!u->next[v])
		u->next[v] = new Node();
		
		u = u->next[v];
		if (u->id == -1)
		u->id = id; 
		//同一个长度n的斐波拉契数,截断为前1~i(1 <= i <= n)位以后,所有的截断方式,都具有相同的序号,表示以该字符串开头的斐波拉契数,对应的序号都是id
	}	
}

int query(char* str)
{
	Node *u = root;
	 // 在树上顺序搜索str的每一个字符
	for (size_t i = 0, len = strlen(str); i < len; i++)
	{
		u = u->next[str[i] - '0'];
		// 若为空集,表示不存以此为前缀的斐波拉契序号 
		if (!u) return -1;
	}
	// 返回以该信息为前缀的斐波拉契序号 
	return u->id;
}

void init()
{
    memset(F, 0, sizeof(F));
    F[0][0] = F[1][0] = 1;
    int s = 0, e = 1;
    add_node((char *)"1", 0);
    add_node((char *)"1", 1);
    for(int i = 2; i < 100000; ++i)
	{
        int p = i%2, q = (i+1)%2;
        for(int j = s; j < e; ++j)
            F[p][j] = F[p][j] + F[q][j];
        for(int j = s; j < e; ++j)
            if(F[p][j]>=10)
			{
                 F[p][j] %= 10;
                 F[p][j+1] += 1;
            }
        if(F[p][e])  ++e;
        if(e - s > 50)  ++s;
        int r = e - 1, cnt = 0;
        memset(Fib, 0, sizeof(Fib));
        while(r >= 0 && cnt<40) //<40的限定,是因为输入不超过40个数字,因此初始化时,我们也只是取前40个数,若位数太多,有可能超过int的范围,导致运行错误(如果不对cnt做限定,则将RE)
            Fib[cnt++] = F[p][r--] + '0';
        add_node(Fib, i);
    }
}

int main()
{
	cin.tie(0);
	cin.sync_with_stdio(false);
	init();
	int t;
	cin >> t;
	for (int i = 1; i <= t; i++)
	{
		cin >> In;
		cout << "Case #" << i << ": " << query(In) << endl;
	}
	
	return 0;
}


posted @ 2017-09-18 08:18  mofushaohua  阅读(130)  评论(0编辑  收藏  举报