ACM 算法艺术与信息学竞赛 1.2.7 超长数字串

题意


顺序打印的字符串

123456789101112131415161718~~~~

要找到给定数字串第一次出现的位置。

比如10第一次出现在10,1011也是出现在10位置。1第一次出现在1。


分析:

按照书上所说的枚举的思想来进行处理。思想主要是分段,比如

123可以分为

1,2,3一个段

也可以分为

3[123]13的一部分

那么在考虑分段时,主要采用以下原则

            /**  如果字符串全是0,那么直接在前面加上一个1,得到其BigInteger的值,并求其位置。
             *       如:0只会出现在10处,00出现在100处,000出现在1000处。
             * 分段时有如下考虑:(假设除头部head,余下部分为next).
             * 注意在分段过程中next的开头不能是以0字符开头。
             * 1、每分段的长度都比字符串长度(len)要小
             *         这里需要分两种情况处理。
             *         A、头部不能取到分段长度:
             *             这里又分两种情况:
             *          a、next部分可以取到或大于分段长度,这个易于处理,
             *          b、next部分不可以取到分段长度,这个不易处理。这个采用与2条件相同的方法来处理,即是只看next部分中的前几位。多出来的位数不要看。
             *         B、头部可以取到分段长度:比如111213,当分段长度为2时,头部可以取到11那么后面的数就顺理成章地推导下去了。但要注意head开头不能是0。
             * 2、每个分段的长度为len,但是头部与尾部均不足len,拼起来才足够。这时next的头部也不是能0开头。
             *         这里需要推导出头部的整个数字,比如99|15,按四位数分的话我们需要推出头部数为14[9915]00。从1499与1500中取了中间四位。
             * 3、整数字符串就是一个数(相当于第二种情况中头部长度为len),不可拆分比如100, 1000,
             *         这时也不可以以0做为开头。
             */
注意一个很重要的原则是,如果可以取整个分段长度的,都不可以以0开头。

粘个java与C++的代码(C++代转自百度博客)

用java主要是考虑到有直接的大数处理。C++呢相对速度很快。

import java.io.*;
import java.util.*;
import java.math.*;

public class Main {
    static final int ms = 250;
	static BigInteger [] beginValue = new BigInteger[ms];
    static final BigInteger negOne = BigInteger.valueOf(-1);
    static final BigInteger nine = new BigInteger("9");
    static BigInteger [] rec = new BigInteger[ms];
    static BigInteger minLength = new BigInteger("0");
//--------------------------------------------------------------------------//
    //取得后面一个数字.
    public static String getNextInt(String a){
        String t = new String("");
        int len = a.length(), v, m = 1;
        for (int i = len - 1; i >= 0; --i){
            char c = a.charAt(i);
            v = (int)(c - 48) + m;
            m = v / 10;
            v %= 10;
            t = (char)(v + 48) + t;
        }
        while (m > 0){
            v = m % 10;
            t = (char)(v + 48) + t;
            m /= 10;
        }
        return t;
    }
    //取得前一个数字
    public static String getFrontInt(String a){
        String t = new String("");
        int len = a.length(), v, m = 1;
        for (int i = len - 1; i >= 0; --i){
            char c = a.charAt(i);
            v = (int)(c - 48) - m;
            if (v < 0){
                v += 10;
                m = 1;
            } else {
                m = 0;
            }
            
            if (!(0 == i && v == 0)){
                t = (char)(v + 48) + t;
            }
        }
        if (t.length() == 0){
            t = "0";
        }
        return t;
    }
    //比较两个字符串看看是否相等:相等则返回共长,否则返回-1
    public static int isEqualString(String a, String line, int pos){
        int i;
        for (i = 0; i < a.length() && (pos + i) < line.length(); ++i){
            if (a.charAt(i) != line.charAt(pos + i)) {
                return -1;
            }
        }
        return i;
    }
    //这里按长度从next的segSize开始比较.
    public static boolean stringCompareBySegSize(String next, int segSize){
        int beginPos = segSize, ret = 0, next_len = next.length();
        String head = next.substring(0, segSize);
        while (beginPos < next_len){
            String back = getNextInt(head);
            ret = isEqualString(back, next, beginPos);
            if (-1 == ret) {
                return false;
            } else {
                beginPos += ret;
                head = back;
            }
        }
        return true;
    }
    //查看是否有进位
    public static boolean isCarryOut(String a){
        int len = a.length();
        for (int i = 0; i < len; ++i){
            if (a.charAt(i) != '9') return false;
        }
        return true;
    }
//    --------------------------------------------------------------------------//
    //rec[]数组记录了每种长度的起始位置:比如两位起始于10,一位起始于1
    public static void genBeginPos(){
        BigInteger [] len = new BigInteger[ms];
        BigInteger ten = BigInteger.ONE;
        len[0] = BigInteger.ZERO;
        rec[0] = BigInteger.ZERO;
        rec[1] = BigInteger.ONE;
        for (int i = 1; i < ms; ++i){
            len[i] = nine.multiply(ten);
            beginValue[i] = ten;
            ten = ten.multiply(BigInteger.valueOf(10));
        }
        for (int i = 2; i < ms; ++i){
            rec[i] = rec[i-1].add(len[i-1].multiply(BigInteger.valueOf(i-1)));
        }
    }
    public static BigInteger getPos(String value){
    	int len = value.length();
    	BigInteger bValue = beginValue[len];
    	BigInteger from = rec[len];
    	BigInteger ret = (new BigInteger(value)).subtract(bValue);
    	ret = ret.multiply(BigInteger.valueOf(len));
    	ret = from.add(ret);
    	return ret;
    }
    
    public static void setMinLength(BigInteger a){
    	if (minLength.compareTo(negOne) == 0 || minLength.compareTo(a) > 0) {
    		minLength = a;
    	}
    }
    
    public static boolean isAllZero(String a){
    	for (int i = 0; i < a.length(); ++i){
    		if (a.charAt(i) != '0') return false;
    	}
    	return true;
    }

    public static void main(String[] args) throws Exception {
        genBeginPos();
        BufferedReader cin = new BufferedReader(new InputStreamReader(System.in));
        String line;
        while ((line = cin.readLine()) != null){
        	if (isAllZero(line)) {
        		line = "1" + line;
        		BigInteger pos = getPos(line);
        		pos = pos.add(BigInteger.ONE);
        		System.out.println(pos.toString());
        		continue;
        	}

            final int line_len = line.length();
            /**
             * 分段时有如下考虑:(假设除头部head,余下部分为next).
             * 注意在分段过程中next的开头不能是以0字符开头。
             * 1、每段的长度都比字符串长度(len)要小
             *         这里需要分两种情况处理。
             *         A、头部不能取到分段长度:
             *             这里又分两种情况:
             *          a、next部分可以取到分段长度,这个易于处理,
             *          b、next部分不可以取到分段长度,这个不易处理。
             *         B、头部可以取到分段长度:比如111213,当分段长度为2时,头部可以取到11那么后面的数就顺理成章地推导下去了。
             * 2、每个分段的长度为len,但是头部与尾部均不足len,拼起来才足够.
             *         这里需要推导出头部的整个数字,比如99|15,按四位数分的话我们需要推出头部数为14[9915]00。从1499与1500中取了中间四位。
             * 3、整数字符串就是一个数(相当于第二种情况中头部长度为len),不可拆分比如100, 1000,
             *    这时也不可以以0做为开头。
             */
            boolean have_find_min_length = false;
            int segSize = 1;
            minLength = negOne;
            for (segSize = 1; segSize < line_len; ++segSize){
                String head = new String(""), next = new String("");
                int head_len = 0, next_len = 0;
                
                minLength = negOne;
                //Condition 1.A
                for (int headSize = 1; headSize < segSize; ++headSize){
                    head = line.substring(0, headSize); head_len = head.length();
                    next = line.substring(headSize, line_len); next_len = next.length();
                    if (next_len > 0 && next.charAt(0) == '0') continue;
                    
                    if (next_len >= segSize){
                        //Condition 1.A.a
                        String value = next.substring(0, segSize);
                        String front = getFrontInt(value);
                        
                        //这里利用next[0~segSize)取得的front值与head进行比较
                        boolean ret = true;
                        for (int i = head.length() - 1, j = front.length() - 1;
                                i >= 0 && j >= 0; --i, --j){
                            if (head.charAt(i) != front.charAt(j)){
                                ret = false;
                                break;
                            }
                        }
                        
                        if (false == ret){
                            //比较失败,直接进行下轮测试。
                            continue;
                        } else {
                            //比较成功,那么从next开始,向后比较。
                            boolean bNextEqualToSegmentSize = stringCompareBySegSize(next, segSize);
                            if (false == bNextEqualToSegmentSize){
                                continue;
                            } else {
                                //这里需要推出完整的头值。
                                String beginValue = getFrontInt(value);
                                BigInteger pos = getPos(beginValue);
                                pos = pos.add(BigInteger.valueOf(beginValue.length() - head.length()));
                                setMinLength(pos);
                                //System.out.println("C 1.A.a Next larger than segSize: value = " + beginValue + " pos = " + pos.toString());
                                //System.out.println("Segment Size = " + segSize);
                            }
                        }
                    } else {
                        //Condition 1.A.b
                        String front_head = next.substring(0, segSize - head_len);
                        boolean bCarryOut = isCarryOut(head);
                        
                        String front_part_value = "";
                        if (bCarryOut){
                            front_part_value = getFrontInt(front_head);
                        } else {
                            front_part_value = front_head;
                        }
                        
                        String frontValue = "";
                        if (front_part_value.compareTo("0") != 0){
                            frontValue = front_part_value + head;
                        } else {
                            frontValue = head;
                        }
                        
                        String NextValue = getNextInt(frontValue);
                        boolean bIsEqual = true;
                        for (int i = 0, j = 0; i < NextValue.length() && j < next_len; ++i, ++j){
                            if (NextValue.charAt(i) != next.charAt(j)){
                                bIsEqual = false;
                                break;
                            }
                        }
                        
                        if (bIsEqual){
                            String beginValue = frontValue;
                            BigInteger pos = getPos(beginValue);
                            pos = pos.add(BigInteger.valueOf(beginValue.length() - head.length()));
                            setMinLength(pos);
                            //System.out.println("C 1.A.b Next small than segSize: value = " + beginValue + " pos = " + pos.toString());
                            //System.out.println("Segment Size = " + segSize);
                        } else {
                            continue;
                        }
                    }
                }
                
                //Condition 1.B
                if (line.charAt(segSize) != '0' && line.charAt(0) != '0'){
                    boolean bHeadEqualToSegmentSize = stringCompareBySegSize(line, segSize);
                    if (bHeadEqualToSegmentSize){
                    	String beginValue = line.substring(0, segSize);
                        BigInteger pos = getPos(beginValue);
                        setMinLength(pos);
                        //System.out.println("C1.B head equal to segSize: value = " + beginValue + " pos = " + pos.toString());
                    }
                }

                
                if (minLength.compareTo(negOne) != 0){
                	have_find_min_length = true;
                	break;
                }
            }
            
            
            //Condition 2
            if (true == have_find_min_length){
            	System.out.println(minLength.toString());
            	continue;
            }
            
            for (int headSize = 1; headSize < line_len && have_find_min_length == false; ++ headSize){
            	String head = line.substring(0, headSize); 
            	final int head_len = head.length();
            	String next = line.substring(headSize, line_len);
            	final int next_len = next.length();
            	if (next_len > 0 && next.charAt(0) == '0') continue;
            	
            	boolean bCarryOut = isCarryOut(head);
            	String beginValue = "";
            	if (bCarryOut) {
            		beginValue = getFrontInt(next) + head;
            	} else {
            		beginValue = next + head;
            	}
            	
                BigInteger pos = getPos(beginValue);
                pos = pos.add(BigInteger.valueOf(beginValue.length() - head.length()));
                setMinLength(pos);
                //System.out.println("C2 add equal to line_len: value = " + beginValue + " pos = " + pos.toString());
                //System.out.println("Segment Size = " + line_len);
            }
            
            //Condition 3
            if (line_len  > 0 && line.charAt(0) != '0'){
                String beginValue = line;
                BigInteger pos = getPos(beginValue);
                setMinLength(pos);
                //System.out.println("C3 hole length: front = " + beginValue + " pos = " + pos.toString());
                //System.out.println("Segment Size = " + line_len);
            }
            
            //最后的输出
            if (minLength.compareTo(negOne) != 0){
            	//System.out.println("Find min length");
            	System.out.println(minLength.toString());
            	continue;
            }
        }
    }
}


C++代码

 #include<cstdio>
 #include<cstring>

 class BigNum
 {
 public:
         BigNum(char *str,int _len)
         {
                 memset(a,0,sizeof(a));
                 len=_len;
                 for(int i=len-1;i>=0;i--) a[i]=(*str++)-'0';
         }
         BigNum(int n)
         {
                 memset(a,0,sizeof(a));
                 for(len=0;n;n/=10,len++) a[len]=n%10;
         }
         operator bool() { return len>0; }
         BigNum operator++()
         {
                 a[0]++;
                 int i;
                 for(i=0;i<len && a[i]>9;i++)
                 {
                         a[i]-=10;
                         a[i+1]++;
                 }
                 if(i==len)
                 {
                         a[i]=1;
                         len++;
                 }
                 return *this;
         }
         void inc()
         {
                 a[0]++;
                 for(int i=0;i<len && a[i]>9;i++)
                 {
                         a[i]-=10;
                         a[i+1]++;
                 }
         }
         friend BigNum operator+(BigNum a,BigNum b)
         {
                 if(a.len<b.len) a.len=b.len;
                 for(int i=0;i<a.len;i++) a.a[i]+=b.a[i];
                 a.ceil();
                 return a;
         }
         friend BigNum operator+(BigNum a,int b)
         {
                 return a+(BigNum)b;
         }
         friend BigNum operator-(BigNum a,BigNum b)
         {
                 for(int i=a.len-1;i>0;i--)
                 {
                         a.a[i]-=b.a[i];
                         a.a[i]--;
                         a.a[i-1]+=10;
                 }
                 a.a[0]-=b.a[0];
                 a.ceil();
                 return a;
         }
         friend BigNum operator-(BigNum a,int b)
         {
                 return a-(BigNum)b;
         }
         friend BigNum operator*(BigNum a,BigNum b)
         {
                 BigNum c=0;
                 c.len=a.len+b.len;
                 for(int i=0;i<a.len;i++)
                         for(int j=0;j<b.len;j++) c.a[i+j]+=a.a[i]*b.a[j];
                 c.ceil();
                 return c;
         }
         friend BigNum operator*(BigNum a,int b)
         {
                 return a*(BigNum)b;
         }
         friend bool operator<(BigNum a,BigNum b)
         {
                 if(a.len==b.len)
                 {
                         int i;
                         for(i=a.len-1;i>0 && a.a[i]==b.a[i];i--);
                         return a.a[i]<b.a[i];
                 }
                 else return a.len<b.len;
         }
         friend bool find(BigNum a,char *str)
         {
                 for(int i=a.len-1;i>=0 && *str;i--)
                         if(a.a[i]!=(*str++)-'0') return false;
                 return true;
         }
         void print()
         {
                 for(int i=len-1;i>=0;i--) printf("%d",a[i]);
                 printf("\n");
         }
         int a[300],len;
 private:
         void ceil()
         {
                 for(int i=0;i<len;i++)
                 {
                         a[i+1]+=a[i]/10;
                         a[i]%=10;
                 }
                 len++;
                 while(len && !a[len-1]) len--;
         }
 };

 bool pd(char *a)
 {
         BigNum t=9,now=0;
         for(char *p=a;*p;p++,t=t*10+9)
         {
                 if(*p!='0') return false;
                 now=now*10+t;
         }
         now=now+2;
         now.print();
         return true;
 }

 char a[210];
 void work()
 {
         int len=strlen(a);
         BigNum t=0,now=0;
         if(pd(a)) return;
         for(int i=1;i<=len;i++,t=t*10+9)
         {
                 now=now+t;
                 BigNum ans=0;
                 for(int j=0;j<i;j++)
                 {
                         if(a[j]=='0') continue;
                         BigNum prev(a,j);
                         prev.inc();
                         if(j>0 && !find(prev,a+i)) continue;
                         char nn[210];
                         int k;
                         for(k=0;k<i && a[j+k];k++) nn[k]=a[j+k];
                         for(;k<i;k++) nn[k]=prev.a[i-k-1]+'0';
                         BigNum n(nn,i),temp=n;
                         int p;
                         for(p=j+i;p<len && find(++n,a+p);p+=n.len);
                         if(p>=len)
                         {
                                 temp=(temp-1)*i-now-j+1;
                                 if(!ans || temp<ans) ans=temp;
                         }
                 }
                 if(ans)
                 {
                         ans.print();
                         break;
                 }
         }
 }

 int main()
 {
         while (scanf("%s",a) != EOF)
         {
			 work();
         }
         return 0;
 }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值