HDU 4099 Revenge of Fibonacci(高精度加法+字典树Trie)

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;
}


你可能感兴趣的:(ACM)