UVa 12333 – Revenge of Fibonacci [大数+字典树]

题目:给你一个数字串,判断他是哪一个Fib数的前缀,有多种答案输出最小的,不存在输出-1。

解题思路:首先题目要求在100000个Fibonacci数之内,说明数字非常大,我们要用大数的方法存储,也就是用char[]来存储每个位上的数字

还有一个关键点是查找,我们使用字典树来解决,这样才可能不超时。字典树的意思就是使用每个节点来存储0-9,节点下面又有子节点,同样存储0-9一直下去例如

-1 -》0 -》0

    -》1 -》0

    -》2

    -》。

    -》。

    -》。

    -》6

    -》7

    -》8

上面简单的树中,10这个数字,就是通过两个节点1,0构成

接下来有几个问题,一个数解决大数相加,例如{8,2,8,1}和{2,4,6}相加,因为他们是用字符数组存储的,他们分别表示数字1828,642(反过来的,这样方便我们相加)

我们先将他们每位分别相加,得到{10,6,14,1}

在逐个处理进位,从10开始,大于9,进位,后一位加一,然后再处理后一位

最后就会变成{0,7,4,2}得出相加结果,再反过来,得到2470


另外一个问题是节点的存储

对于2470,我们从根节点开始查找,没有2这个节点,就新增,以此类推

另外java会OOM。。。

package test;

import java.util.Arrays;
import java.util.Scanner;

public class Test{	
	static class Node{//节点,构成字典树
	    int id;
	    Node next[] = new Node[10];//每个节点下面有10个节点,分别存储0-9
	    Node(){
	        id = -1;
	        for(int i = 0; i < 10; ++i)//初始化子节点
	            next[i] = null;
	    }
	};
	static char Fib[] = new char[50];//Fib用字符数组来存储大数据,例如{1,2,3,4}来不是1234
	static int F[][] = new int[2][1024000];//Fibonacci数列,存储每次运算的前两个数(也就是构成下一个数的两个加数)
	static Node root = new Node();//根节点
	
	static void add_node(char[] str, int id){//添加新节点
	    Node u = root;
	    int len = 0;
	    for(;len<str.length;len++){//找到大数据的有效长度
	    	if(str[len]=='\0'){
	    		break;
	    	}
	    }
	    
	    for(int i = 0; i < len && i <= 40; ++i){//逐个存储
	        int v = str[i] - '0';//字符减去'0',计算出对应int值
	        
	        if(u.next[v]==null)
	            u.next[v] = new Node();
	        u =  u.next[v];
	        if(u.id == -1)
	            u.id = id;
	    }
	}
	
	static int query(char[] str){//查找		
	    Node u = root;	   
	    for(int i = 0,len=str.length; i < len; ++i){
	        u = u.next[str[i]-'0'];
	        if(u==null) return -1;
	    }
	    return u.id;
	}
	
	public static void main(String[] args) {
		
		F[0][0] = F[1][0] = 1;//初始化前两个数
	    int s = 0, e = 1;//s表示字符数组的起始位置,e表示字符数组的结束位置
	    add_node(new char[]{'1'}, 0);//添加前两个节点
	    add_node(new char[]{'1'}, 1);
	    
	    for(int i = 2; i < 100000; ++i){//生成100000个Fibonacci数,并且存储
	        int p = i%2, q = (i+1)%2;//利用余数性质
	        
	        for(int j = s; j < e; ++j)
	            F[p][j] = F[p][j] + F[q][j];//将前两个数每一位都相加
	        /*
	         * 判断每一位相加后是否大于10,如果是,说明要进位;注意,这里是反向存储数字的
	         * 也就是说,{4,3,2,1},表示的是1234
	         */
	        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]!=0)  ++e;
	        if(e - s > 50)  ++s;
	        
	        int r = e - 1, cnt = 0;
	        Arrays.fill(Fib, '\0');//清空fib数组	  
	        //反向存储,就是吧{4,3,2,1}变成{1,2,3,4}存储起来
	        while(r >= 0 && cnt<45)
	            Fib[cnt++] = (char) (F[p][r--] + '0');
	        
	        add_node(Fib, i);//添加节点
	    }
	   Scanner scanner = new Scanner(System.in);
	   String line;
	   while(!"".equals((line=scanner.nextLine()))){	        
	       System.out.println(query(line.toCharArray()));
	    }
	}
}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值