HDU 4099 Revenge of Fibonacci(高精度加法+字典树Trie)
http://acm.hdu.edu.cn/showproblem.php?pid=4099
题意:
给你一个数,这个数是斐波那契数列中的一个数的前缀,要你找出满足这个前缀的最小下标.且这个下标如果查过了10W,还没有符合要求的数,就输出-1.且输入数不会查过40位,且没有前导0。
分析:
直觉的思路是:将第0到第10W个斐波那契数的前40位都保存为字符串构建字典树。然后对于每个输入串判断是否在该字典树即可。
但是:第10W个斐波那契数大概有2W位整数,我们不可能把所有10W个斐波那契数都算出来(因为超长整数加法太慢)。
所以:我们采取的策略是对于每个斐波那契数我们只保存它的前60位(虽然我们需要前40位),然后后一个数在前两个数的前60位基础上算出。
这种方法是有问题的(但是出现问题的概率很低很低).因为它舍弃了60位后的所有位,如果这后面的位有进位呢?比如假设我们要取前4位,现在我们保存前6位:
11114455555
11115555555
没舍弃前6位之后的数值时:前4位的结果是2223
如果舍弃了前6位后的数值,前4位相加的结果是2222.
所以上面方法是有问题的,但是我们例子中要的是前40位,我们取前60位,只要我们第41位-60位有两个对应位相加有进位,就可以无视60-1000位的进位.这里由于我们就算的数据特殊40-60位肯定是有进位的.
注意:我们程序代码中的实现是,只要第二个数的位数一超过60位(正好61位)我们就同时截断第一个和第二个数的最后一位(最低位),可能就变成了59+60位或60+60位的情况了.所以这个时候我们只需要把这两个数从低位(字符数组的最后一位)一只加到高位即可。
然后对于每个输入的字符串s,我们在Trie树中找与之对应的数下标即可.注意:如果我们在Trie没有找到包含s的串,那就输出-1,如果我们找到了以s为前缀的串,我们就继续向下走到底,找出所有以s为前缀的单词的斐波那契下标,然后输出最小的那个下标即可。
AC代码:(如果把代码中的len2>60 改成len2>45 就会WA 证明了我的论点)
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; char temp[100];//中间结果 void add(char *a,char *b,char *c) { int len1=strlen(a); int len2=strlen(b); int i=len1-1,j=len2-1; int carry=0;//进位 int k=0;//中间结果temp的位数 while(i>=0||j>=0) { int x,y,z; if(i<0) x=0; else x=a[i]-'0'; if(j<0) y=0; else y=b[j]-'0'; z=x+y+carry; temp[k++]=z%10+'0'; carry=z/10; i--; j--; } if(carry) temp[k++]=carry+'0';//别忘了这个进位 for(int i=0;i<k;i++)c[i]=temp[k-1-i]; c[k]=0; } struct trie { trie *next[10]; int v;//root节点的v始终为-1,其他节点保存的是经过该节点的最小id号 void init() { v=-1; for(int i=0;i<10;i++) next[i]=NULL; } }; trie *root; void insert(char *s,int val) { int n=strlen(s); trie *p=root,*q; for(int i=0;i<n && i<40;i++) { int id=s[i]-'0'; if(p->next[id]==NULL) { q=(trie*)malloc(sizeof(trie)); q->init(); p->next[id]=q; } p=p->next[id]; if(p->v<0) p->v=val; } } int find(char *s) { int n =strlen(s); trie *p=root,*q; for(int i=0;i<n;i++) { int id=s[i]-'0'; if(p->next[id]==NULL) { return -1; } p=p->next[id]; } return p->v; } char str[3][100]; int main() { root = (trie*)malloc(sizeof(trie)); root->init(); str[0][0]='1'; str[0][1]=0; insert(str[0],0); str[1][0]='1'; str[1][1]=0; for(int i=2;i<100000;i++) { int len1=strlen(str[0]); int len2=strlen(str[1]); if(len2>60) { str[0][len1-1]=0; str[1][len2-1]=0; } add(str[0],str[1],str[2]); insert(str[2],i); strcpy(str[0],str[1]); strcpy(str[1],str[2]); } int T; scanf("%d",&T); for(int kase=1;kase<=T;kase++) { scanf("%s",str[0]); int ans=find(str[0]); printf("Case #%d: %d\n",kase,ans); } return 0; }