UVA-12333 Revenge of Fibonacci(竖式加法模拟 & 字典树)

题目:

给出一个斐波那契数字的前缀,问第一个有这个前缀的数字在斐波那契数列中是第几个。

思路:

紫书提示:本题有一定效率要求。如果高精度代码比较慢,可能会超时。

利用滚动数组和竖式加法来模拟斐波那契相加的过程,在这个过程中每得出一个斐波那契数字就用字典树存一下。

PS:在滚动数组中存的斐波那契数字是逆序存储的。

代码:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define MAX 1e9;
#define FRE() freopen("in.txt","r",stdin)
#define FRO() freopen("out.txt","w",stdout)
using namespace std;
typedef long long ll;
const int maxn = 4000010;
struct Node {
    int next[10];
    int id;
    Node() {
        for(int i = 0; i<10; i++) {
            next[i] = -1;
        }
        id = -1;
    }
};
Node trie[maxn];
int tot = 0;
//Node root = trie[tot++];

int idx(char ch) {return ch-'0';}

void Insert(char* str, int id) {//字典树插入
//    int u = 0;
//    for(int i = 0; i<strlen(str); i++) {
//        int v = idx(str[i]);//获取字符串当前位置的数字
//        if(trie[u].next[v]==-1) { //如果这个位置是不存在的
//            trie[u].next[v] = tot;
//            u = tot++;
//        } else { //如果这个位置是存在的
//            u = trie[u].next[v];
//        }
//        if(trie[u].id==-1) {
//            trie[u].id = id;
//        }
//    }
    int u = 0;
    for(int i = 0; i<strlen(str); i++){
        int v = idx(str[i]);
        if(trie[u].next[v]==-1){//如果这个位置不存在
            trie[u].next[v] = tot++;
        }
        u = trie[u].next[v];//结点往下走
        if(trie[u].id==-1){//保存这个斐波那契数字的id
            trie[u].id = id;
        }
    }
}

int Query(char* str) {
    int u = 0;
    for(int i = 0; i<strlen(str); i++) {
        int v = idx(str[i]);
        if(trie[u].next[v]==-1) { //说明这个前缀不会出现
            return -1;
        }
        u = trie[u].next[v];
    }
    return trie[u].id;
}


int fib[2][maxn];
int main() {
    FRE();
    int s=0,l=1;
    char f[55];
    Insert("1",1);
    fib[1][0] = 1;
    fib[0][0] = 0;
    for(int i=2; i<100000; i++) {
        int a=i%2, b=(i+1)%2;//滚动数组,可以手动跑两组斐波那契数字的相加就可以理解了
        for(int k=s; k<l; k++) {
            fib[a][k] = fib[a][k] + fib[b][k];
            if(fib[a][k]>=10) { //这位相加可以进位时
                fib[a][k+1]++;//下一位加一
                fib[a][k] -= 10;//这一位减掉进位的10
                if(k==l-1) { //最后一个进位让l加一
                    l++;
                }
            }
        }
        if(l-s>50) { //数组中是倒着存这个数的,l-1表示个位开始,s表示最高位
            s++;
        }
        int cnt=0;
        for(int k=l-1; k>=s&&cnt<40; k--){//根据题目提示,只选取前40位即可
            f[cnt++] = fib[a][k]+'0';
        }
//        if(i==10)
//            printf("%s
",f);
        Insert(f,i);
        memset(f,0,sizeof(f));
    }
    //cout<<"GG"<<endl;
    char str[55];
    int n;
    scanf("%d",&n);
    for(int i=0; i<n; i++){
        scanf("%s",str);
        printf("Case #%d: ",i+1);
        int ans = Query(str);
        printf("%d
",ans==-1 ? -1:ans-1);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/sykline/p/10285500.html