兩個來自JAVAEYE的題目

假設有這樣一種字符串,它們的長度不大於 26 ,而且若一個這樣的字符串其長度爲 m ,則這個字符串必定由 a, b, c ... z 中的前 m 個字母構成,同時我們保證每個字母出現且僅出現一次。比方說某個字符串長度爲 5 ,那麼它一定是由 a, b, c, d, e 這 5 個字母構成,不會多一個也不會少一個。嗯嗯,這樣一來,一旦長度確定,這個字符串中有哪些字母也就確定了,唯一的區別就是這些字母的前後順序而已。

 

現在我們用一個由大寫字母 A 和 B 構成的序列來描述這類字符串裏各個字母的前後順序:

如果字母 b 在字母 a 的後面,那麼序列的第一個字母就是 A (After),否則序列的第一個字母就是 B (Before);
如果字母 c 在字母 b 的後面,那麼序列的第二個字母就是 A ,否則就是 B;
如果字母 d 在字母 c 的後面,那麼 …… 不用多說了吧?直到這個字符串的結束。

這規則甚是簡單,不過有個問題就是同一個 AB 序列,可能有多個字符串都與之相符,比方說序列“ABA”,就有“acdb”、“cadb”等等好幾種可能性。說的專業一點,這一個序列實際上對應了一個字符串集合。那麼現在問題來了:給你一個這樣的 AB 序列,問你究竟有多少個不同的字符串能夠與之相符?或者說這個序列對應的字符串集合有多大?注意,只要求個數,不要求枚舉所有的字符串。 

最後總結一下吧:

實際上這個問題的算法直接做是 O(n^3) 的,做些優化之後可以到 O(n^2)。這裏的關鍵是隻考慮可能性而不要去想具體如何插入:請在腦子裏面建一張三角形的表,每一行對應於 AB 字符串中的一個字符,每一行中第 i 個元素代表了到目前爲止最後一個字母放在第 i 個位置的可能性個數。比方說:

 

代碼
 
  1. A:            0      1  
  2. B:       1       1      0  
  3. A:   0       1       2       2  
<script type="text/javascript">render_code();</script>

 

這裏第一行 A ,當前最後一個字母是 b ,b 放在第 0 個位置的可能性個數是 0 ,放在第 1 個位置的可能性個數是 1 ;第二行 B ,當前最後一個字母是 c ,放在第 0、1 兩個位置的可能性個數都是 1 ,放在最後一個位置的可能性是 0 …… 依此類推。很容易可以看出,如果當前行的次序是 A ,那麼放在第 i 個位置的可能性就是上一行從 0 到 i-1 位置的可能性個數之和,否則就是從 i 到最後一個位置的可能性個數之和。逐步向下做,直到用完輸入的 AB 字符串即可,最後的總數就是對上述表格最後一行求個和。這裏每一行中各個列的可能性是沒有重疊的,因爲最後一個字母的位置各不相同。

複雜度麼,這個表格一共有 O(n^2) 項,每一格構造最多花 O(n) ,所以總複雜度自然是 O(n^3) ,稍做優化可以把計算每一格的開銷降到 O(1) ,複雜度可以降到 O(n^2) 。這裏給個參考實現 —— C++ 寫的,不熟悉的朋友只能說見諒了,好在還算簡單,沒用什麼特殊的東西:

 

代碼
 
  1. int count(const string& AB)   
  2. {   
  3.   vector<int> prev_line;   
  4.   vector<int> current_line;   
  5.   
  6.   prev_line.push_back(1);   
  7.   for( string::const_iterator it=AB.begin();   
  8.        it!=AB.end();   
  9.        ++it) {   
  10.   
  11.     current_line.clear();   
  12.     current_line.resize(prev_line.size()+10);   
  13.   
  14.     if( *it == 'A' ) {   
  15.       int possibility = 0;   
  16.       for(size_t i=0; i<prev_line.size(); ++i) {   
  17.         current_line[i] = possibility;   
  18.         possibility += prev_line[i];   
  19.       }   
  20.       current_line.back()=possibility;   
  21.     }   
  22.     else if( *it == 'B' ) {   
  23.       int possibility = 0;   
  24.       for(size_t i=prev_line.size(); i>0; --i) {   
  25.         current_line[i] = possibility;   
  26.         possibility += prev_line[i-1];   
  27.       }   
  28.       current_line[0]=possibility;   
  29.     }   
  30.     else {   
  31.       assert(false);  // invalid input   
  32.     }   
  33.     prev_line.swap(current_line);   
  34.   }   
  35.   
  36.   int result = 0;   
  37.   for( vector<int>::iterator it=prev_line.begin();   
  38.        it!=prev_line.end();   
  39.        ++it ) {   
  40.     result += *it;   
  41.   }   
  42.   return result;   
  43. }  
<script type="text/javascript">render_code();</script>

 

其中 vector 是 C++ 標準庫的成員,實際上就是變長數組。我用 prev_line 和 current_line 分別記錄上述三角形表格的前一行和當前行,最後對最後一行求一個總和。

有一個整數n,寫一個函數f(n),返回0到n之間出現的"1"的個數。比如f(13)=6,現在f(1)=1,問下一個最大的f(n)=n的n是什麼?

爲什麼f(13)=6, 因爲1,2,3,4,5,6,7,8,9,10,11,12,13.數數1的個數,正好是6.

我把c的代碼貼出來!

他計算到4000000000,用的是剪枝。

 

代碼
 
  1. #include "stdafx.h"  
  2.   
  3. #include <windows.h>   
  4. #include <stdlib.h>   
  5.   
  6. int f(int n);   
  7. int count1(int n);   
  8. int cal(unsigned int number,int nwei,int count1,unsigned int ncount);   
  9.   
  10. int gTable[10];   
  11. const unsigned int gMAX = 4000000000L;   
  12.   
  13. int main(int argc, char* argv[])   
  14. {   
  15.   int i;   
  16.   unsigned int n=1;   
  17.   unsigned int ncount = 0;   
  18.   int nwei = 0;   
  19.   int ncount1;   
  20.   
  21.   /*if(argc>1)  
  22.   {  
  23.     n = atoi(argv[1]);  
  24.     ncount = f(n);  
  25.     printf("f(%d) = %d/n",n,ncount);  
  26.   }*/  
  27.   
  28.   int beginTime=GetTickCount();   
  29.   //init gTable   
  30.   for(i=0;i<10;++i)   
  31.   {   
  32.     n *= 10;   
  33.     gTable[i] = f(n-1);   
  34.   }   
  35.   
  36.   n=0;   
  37.   nwei = 0;   
  38.   ncount1 = 0;   
  39.   while(n<gMAX)   
  40.   {   
  41.     unsigned int temp;   
  42.        
  43.     temp = 1;   
  44.       
  45.     ncount =cal(n,nwei,ncount1,ncount);   
  46.     for(i=0;i<nwei;++i)   
  47.       temp *= 10;   
  48.     n += temp;   
  49.     if( (n/temp)/10 == 1)   
  50.       ++nwei;   
  51.     ncount1 = count1(n);   
  52.   }   
  53.   
  54.   int endTime=GetTickCount();   
  55.   endTime-=beginTime;   
  56.   
  57.   printf("time: %d ms/n",endTime);   
  58. return 0;   
  59. }   
  60.   
  61. int f(int n)   
  62. {   
  63.   int ret = 0;   
  64.   int ntemp=n;   
  65.   int ntemp2=1;   
  66.   int i=1;   
  67.   while(ntemp)   
  68.   {   
  69.     ret += (((ntemp-1)/10)+1) * i;   
  70.     if( (ntemp%10) == 1 )   
  71.     {   
  72.       ret -= i;   
  73.       ret += ntemp2;   
  74.     }   
  75.     ntemp = ntemp/10;   
  76.     i*=10;   
  77.     ntemp2 = n%i+1;   
  78.   }   
  79.   return ret;   
  80. }   
  81.   
  82. int count1(int n)   
  83. {   
  84.   int count = 0;   
  85.   while(n)   
  86.   {   
  87.     if( (n%10) == 1)   
  88.       ++count;   
  89.     n /= 10;   
  90.   }   
  91.   return count;   
  92. }   
  93.   
  94. int cal(unsigned int number,int nwei,int count1,unsigned int ncount)   
  95. {   
  96.   int i,n=1;   
  97.   unsigned int maxcount;   
  98.   if(nwei==0)   
  99.   {   
  100.     ncount += count1;   
  101.     if(number == ncount)   
  102.     {   
  103.       printf("f(%d) = %d /n",number,number);   
  104.     }   
  105.     return ncount;   
  106.   }   
  107.   for(i=0;i<nwei;++i)   
  108.     n *= 10;   
  109.   maxcount = ncount + gTable[nwei-1];   
  110.   maxcount += count1*n;   
  111.   if(ncount > (number +  (n-1)) )   
  112.   {   
  113.    return maxcount;   
  114.   }   
  115.   if(maxcount < number)   
  116.   {   
  117.     return maxcount;   
  118.   }   
  119.   n /= 10;   
  120.   for(i=0;i<10;++i)   
  121.   {   
  122.     if(i==1)   
  123.        ncount = cal(number+i*n,nwei-1,count1+1,ncount);   
  124.     else  
  125.        ncount = cal(number+i*n,nwei-1,count1,ncount);   
  126.   }   
  127.     return ncount;   

  128. 我們將數x表示成 head*10(n)+tail;
    如:x=1234 , 則表示爲 1* 10(3) + 234;
    f(1234) = 1 * f(99) + 235 + f(234);
    不用我解釋吧
    234 = 2 * 10(2) + 34;
    f(234) = 2 * f(99) + 100 + f(34);
    從1到234,有兩次 f(99) ,從 100 到 199 共有100個1, 從 200到234 的 1的總和 = f(34)

    可以得出:
    對於數x,
    如果x<10
    f(x) = x>=1 ? 1 : 0;
    如果head = 1
    f(x) = head * f(10(n) - 1) + f(tail) + tail+1
    如果head > 1
    f(x) = head * f(10(n) - 1) + f(tail) + 10(n);

    根據這個公式我們可以迅速的求出f(x),而如果用getCountOfOne(long number),只能得到某數中1的個數,對於從1到n的值,必須使用循環相加的方法,如果計算,123456789的值的話,就需要循環 123456789次,即便剪枝,也不能減少多少循環次數, 而用這個公式,寫個遞歸方法,可以很快的算出答案。

    但仍然有個問題:
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章