微软必应·英雄会第三届在线编程大赛 “几个bing” 题解

来源:互联网 发布:旁氏米粹洗面奶知乎 编辑:程序博客网 时间:2024/04/29 04:02

       新年之初,登陆CSDN看到了英雄会的题目,好久没有头脑风暴了,一看题目还算掌握中,决定试试,于是开始分析题目了。

       题目要求: 例如有一个字符串"iinbinbing",截取不同位置的字符‘b’、‘i’、‘n’、‘g’组合成单词"bing"。若从1开始计数的话,则‘b’ ‘i’ ‘n’ ‘g’这4个字母出现的位置分别为(4,5,6,10) (4,5,9,10),(4,8,9,10)和(7,8,9,10),故总共可以组合成4个单词”bing“。

      问题是:现给定任意字符串,只包含小写‘b’ ‘i’ ‘n’ ‘g’这4种字母,请问一共能组合成多少个单词bing?

     字符串长度不超过10000,由于结果可能比较大,请输出对10^9 + 7取余数之后的结果。

   

      分析了一下,可以开创四个数组,分别记录b,i,n,g的位置,通过位置进行比较求解。

      先定义四个vector<int> 对象:

     

vector<int> b;vector<int> i;vector<int> n;vector<int> g;
        
对于任意一个输入字符串input,先进行如下操作,
 
int len = input.length();        for(int j = 0;j<len;j++)        {            x = input[j] - 'a';             switch(x)            {                case 1:    //‘b'                    b.push_back(j);                    break;                case 8:   // 'i'                    i.push_back(j);                    break;                case 13: //'n'                    n.push_back(j);                    break;                case 6:  //'g'                    g.push_back(j);            }        }  

       如给定输入input = "iinbinbing";
       b[] = { 3,6 }
        i[] = { 0,1,4,7 }
       n[] = { 2,5,8 }
       g[] = { 9 }
       算法一:
       通过嵌套循环遍历 b[],i[],n[],g[],当数组中存放的值为递增序时,结果+1,复杂度O(n^4),代码略。
       程序复杂度分析,由于输入长度最多为10000,因此所有测试数据中,复杂度最大的为2500^4,即是
      bbb.........iii............nnn............ggg.........
      (各2500个)
      而对于2500^4 = 39062500000000 , 是一个非常庞大的数据。因此该算法必然导致超时。
   
     算法二:
     增加定义四个数组:
vector<long long> result_b;         vector<long long> result_i;         vector<long long> result_n;         vector<long long> result_g; 

    每个result_x数组中的值分别表示对应x数组中值所包含的可行解,result_x初始化为:
 
int len_b = b.size();        result_b.assign(len_b,0);        int len_i = i.size();        result_i.assign(len_i,0);        int len_n = n.size();        result_n.assign(len_n,0);        int len_g = g.size();        result_g.assign(len_g,1);  

如:
        b[] = { 3,6 }
        i[] = { 0,1,4,7 }
       n[] = { 2,5,8 }
       g[] = { 9 }
     ====>
 result_g[] = {1}   ,  可行解就是本身
 result_n[] = {1,1,1} ,  对于n[0]=2来说,g[]中有1个值(9)比2大,因此result_n[0] = result_g[0]
 result_i[] = {3,3,2,1}, 同理,对于i[2]=4来说,n[]中有2个值(5,8)比4大,因此result_i[2] = result_n[1]+result_n[8]=2
 result_b[] = {3,1} ,  对于b[0] = 3来说,i[]中有两个值(4,7)比3大,因此result_b[0] = result_i[2] + result_i[3] = 3
      最后对result_b[]求和即是最后的结果。

      因此把上面的四重循环分解为三个二重循环,第一次n[]和g[]循环,第二次i[]和n[]循环,第三次b[]和i[]循环。复杂度为O(n^2),最坏数据为biii.......nnn.......g,其中有连续两个字符为4999个,最坏计算次数约为5000^2 = 25000000,按理说在10^8下应该很快运行结束,按照该算法进行挑战,提交后发现还是超时。
      利用程序构造字符串测试了一下,大概得花10秒左右,想到可能是测试数据太多在vector要进行不断的创建造成,精简了一些if语句,比如在第一个二重循环时,数据不可能大于10^9,省略了该步骤的条件判断,再次提交,还是超时。
       由于已经挑战失败,只能当学习再试试改进。    

       算法三:
       在算法二的基础上,增加了标记,分析在求解result_x[]数组时,result_x最后的结构必然是一个非递减序列,因此求result_x[i]的值均可以用到result_x[i-1]进行快速求解,省去了对第二层数组的累加。
     比如在上例:
        b[] = { 3,6 }
        i[] = { 0,1,4,7 }
    再求得result_i[] = {3,3,2,1}时,求result_b[]时,最多只需要遍历i[]2次即可。
    第一次计算sum(result_i[]) = 9;
    第二次计算result_b[] 具体如下:
    定义一个位置site初始指向i[0],若b[0] > i[site],num -= result_i[site]且site++,直到b[0]<i[site],result_b[0] = num
   代码实现为
num = sum(result_i);        site = 0;        for(int j = 0;j<len_b;j++)        {                while(site<len_i && b[j]>i[site])                        {                num -= result_i[site];                site++;            }            result_b[j] = num;        } 


     这样对于b[] ,i[] 相当于各自只需要遍历一次,算法复杂度O(n)。 
     最后只需要计算 c = sum(result_b);  即可。
     由于结果较大,需要用大整数,但是分析了最大数据小于long long类型(64位),因此定义计数c为long long类型,最后取模即可,最后提交顺利通过。   

    源代码下载地址 :http://download.csdn.net/detail/longteng1116/6801531

2 0
原创粉丝点击