百度2014校招筆試題目題解(更新了第1題的算法,10.9下午)


百度2014校招筆試題目題解

                                                                     ----武漢站,9.28號百度校招筆試題目算法題目部分

二、算法與程序設計題

1、給定任意一個正整數,求比這個數大且最小的“不重復數”,“不重復數”的含義是相鄰兩位不相同,例如1101是重復數,而1201是不重復數。(15分)

2、長度為N(N很大)的字符串,求這個字符串里的最長回文子串。(15分)

3、數軸上從左到右有n各點a[0], a[1], ……,a[n -1],給定一根長度為L的繩子,求繩子最多能覆蓋其中的幾個點。(15分)

 

這是百度的筆試題目,好像是什么系統行為分析師職位的筆試題目!博文最后會貼出試題照片!

 

題解:(題解非官方,僅供參考,轉載請聯系博主,有錯誤的地方望指正!謝謝)

1、給定任意一個正整數,求比這個數大且最小的“不重復數”,“不重復數”的含義是相鄰兩位不相同,例如1101是重復數,而1201是不重復數。

解: 這道題目我也沒有什么特別出彩的算法,就是按照常規思路來求解,首先要理解什么叫做“不重復數”,這是解題的關鍵之一,相鄰兩位不相同的數即為“不重復數”;還有一個地方要注意的就是“求比這個數大且最小的”,就是在比給定數大的不重復數中找到最小的!理解了這兩個地方,這道題目就可以着手求解了!

  我用一個實例來說說我的思路,加入給定的正整數為11233:

  用最常規的方法,就是每次對這個數加1,判斷是不是“不重復數”,加1后為11234,有兩個1重復了,於是繼續加1,……,那么主要就在判斷是不是“不重復數”上面了,我設置了兩個變量,是currentBit, lastBit,顧名思義,lastBit保存上一個數位的值,currentBit保存當前數位的值,拿整數12345來說,就是初始化lastBit = 5,然后計算currentBit = 4;下一步,把currentBit 值賦給lastBit,即lastBit = 4,計算currentBit  = 3;依次類推。

  說到這里,很多朋友覺得可以有更加高效的方法,小難點有4:

  1、因為可以直接定位到“重復”的數位那里,比如12344,我們可以直接定位到個位數4和十位數4上面,然后在低位數(這里就是個位數)上加1即可,事實上,不是如此簡單,假設是對整數12199呢?還能簡單的加1操作嗎,加1之后結果為12200,那結果顯然是錯的,當然這也是可以處理的,處理方法就是把12200繼續拿到循環去判斷是不是“非重復數”;

  2、這里又產生一個問題,因為“重復“的數位可能不止一對,有可能有多對,比如整數11233,定位“33”之后變為“34”,定位“11”之后變為“12”,結果就是12234。又有重復的地方,就是“22”。繼續拿到循環去重復;

  3、還有要注意的是,你的程序設計,是如何存儲數位的值的,是一個一個數位的值求出來呢?還是像我這樣設置一個前后索引?每個數位取值,程序會變得繁瑣復雜,不易讀懂,如果是設置前后索引,則要注意程序退出循環的條件!

  4、對於存在多對“重復”數位的正整數,數位“重復”的定位和變換,是從高位到低位,還是從低位到高位呢?正確的應該是從高位到低位,這與我們的程序設計也帶來了不便。

  這些就是我在試圖找到高效算法時得到的經驗,每個小難點都要處理,有點繁瑣,有耐心的朋友,可以試着寫一下更高效的算法,另外,使用了比較高效的算法,代碼盡量保持簡潔,並告知博主,謝謝!

  注:評論網友13樓“巴魯斯”給出了高效的C#算法代碼,這個算法我當初也考慮了,我主學C,只是C處理字符串沒有C#、Java那么方便,類型轉換也比較麻煩,就沒去管,十分感謝“巴魯斯”朋友的code!來自評論中24樓朋友“garbageMan”給出了比較簡潔的c語言代碼,采用遞歸方法,值得借鑒!再次感謝!

  my code:(簡單加1的方法)

/*
    給定任意一個正整數,求比這個數大且最小的“不重復數”,“不重復數的含義是相鄰兩位不相同,例如1101是不重復數”

*/
#include <stdio.h>
#include <stdlib.h>
int getNumNonrepetition(const int NumGived)
{
    int flag = 0;//為0表示該數不是“不重復數”
    int numRepeat = NumGived;
    int numTemp = 0;//
    int currentBit = 0, lastBit = 0;//前后數位索引
    while(1)
    {
        numRepeat++;
        //初始化后索引
        numTemp = numRepeat;
        lastBit = numTemp % 10;
        numTemp /= 10;
        flag = 1;
        //判斷該數是不是“非重復數”
        while(numTemp != 0)
        {
            currentBit = numTemp % 10;
            numTemp /= 10;
            if(lastBit == currentBit)
            {
                flag = 0;
                break;
            }
            lastBit = currentBit;
        }
        if(flag == 1)//該數為不重復數,返回
        {
            return numRepeat;
        }
    }
}
int main(void)
{
    int NumGived = 19922884;
    int result = getNumNonrepetition(NumGived);
    printf("the number is %d\n", result);
    return 0;
}
View Code

 

更新內容:(10.9號下午)

簡單加1的算法,效率太低,看到這么多的朋友的評論,大家的算法大同小異,我也寫了一個算法,拿出來和大伙分享。

算法:

1、把整數放到字符數組里面去,從高位為低位(用變量i)掃描,找到重復的數位,重復數位為“99”跳到第2步,否則跳到第3步,若沒有重復的數位,則該數為不重復數,返回;

2、遇到“99”的重復數,則把“99”改為“00”,然后在“99”前面一位字符加1,把掃描的地方定位在“99”往高位方向的第2位,比如是1299,變換后為1300,然后把掃描變量 i 定位在1這一數位上,返回第1步;

3、遇到非“99”的重復數,則直接在低位加1,后面依次變為010101……,結果就是最小的不重復數,返回改值;

至於前面說的一些難點,真是害怕誤導了大家,畢竟總有考慮不到的地方,希望見諒!

code:(高效)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 100
int getNumNonrepetition(const long long  NumGived, char NumStr[])
{
    int NumTmp = NumGived;
    int NumLength = 0;
    int i = SIZE - 1;
    //把整數放到字符數組里面去,從后往前放,比如1234,
    //那么數組NumStr[96] = 1  NumStr[97] = 2  NumStr[98] = 3  NumStr[99] = 4, SIZE = 100
    do
    {
        NumStr[i] = NumTmp % 10 + '0';
        NumTmp /= 10;
        i--;
    }while(NumTmp != 0);
    NumLength = SIZE - i - 1;//計算整數的位數

    int flag = 0;//設置010101的時候用的變量
    i = SIZE - NumLength;
    while( 1 )
    {
        //定位到重復的位上面,下標i + 1為低位,此時NumStr[i] == NumStr[i + 1]
        while(i + 1 < SIZE && NumStr[i] != NumStr[i + 1]) i++;
        if(i == SIZE - 1) break;//掃完一遍,沒有重復的,跳出循環,該數是不重復數

        if(NumStr[i + 1] == '9')//重復的數位為99這種情況,將這兩位全部置0,高位加1
        {
            NumStr[i + 1] = '0';
            i--;
            NumStr[i + 1] = '0';
            i--;
            NumStr[i + 1] += 1;
        }
        else//重復的
        {
            //低位加1
            NumStr[i + 1] += 1;
            i += 2;
            flag = 0;
            //后續全部設為0101……,這個時候肯定是不重復數了,所以可以跳出循環
            while( i < SIZE )
            {
                NumStr[i] = flag % 2+ '0';
                flag++;
                i++;
            }
            break;
        }
    }
    //打印最小的”不重復數“
    int start = SIZE - NumLength;
    //如果是99開頭的數字,高位可能會進位,判斷是否為零,不為零則有進位,需打印出來
    if(NumStr[start - 1] != '0') putchar(NumStr[start - 1]);
    for(i = start; i < SIZE; i++ )
    {
        putchar(NumStr[i]);
    }
    return 0;
}
int main(void)
{
    long long  NumGived = 119998988;
    char NumStr[SIZE];
    memset(NumStr, '0', SIZE * sizeof(char));
    getNumNonrepetition(NumGived, NumStr);
    return 0;
}
View Code

 

2、長度為N(N很大)的字符串,求這個字符串里的最長回文子串。

解:題目指出“N很大”,就是提示我們不要想通過遍歷的方法來找到這個字符串,我想到的就一種解法,時間復雜度應該不高,但是我算不出來這個算法的復雜度是多少,首先說一下什么是回文字符串:回文字符串是指從左到右和從右到左相同的字符串,比如"1221"或者“12321”都是回文字符串。剛好舉得這兩個回文字符串的例子就是我的算法的兩個類別:

  第一類“12321”:中間是一個單獨的字符。算法的思想是從第2個字符直到倒數第2個字符遍歷,每遇到一個字符,就依次判斷這個字符前后的字符是否相等,如果相等,則繼續判斷下一個字符,直到以這個字符為中心的兩邊對稱的字符不相等為止,或者前后字符的位置數組越界為止;計算此時的回文字符串的長度,與之前的比較,記下較長的回文字符串的長度和中心字符的位置;遍歷結束則返回最大長度和中心字符的位置

  圖示:若字符串為“1234321”

 

   第二類“123321”:中間是兩個相同的字符。算法思想同上,其實是一樣的過程!圖解也是一樣的!

my code:

 

/*
    長度為N(N很大)的字符串,求這個字符串里的最長回文子串。
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//第一類“12321”:中間是一個單獨的字符
int  FindLongPaliSubstr_Odd(const char A[], int * indexMid)
{
    int i = 0, cnt = 0;//cnt表示前后移動位數
    int MyMax = 0;
    int lenOfA = strlen(A);
    *indexMid = 0;
    for(i = 1; i <= lenOfA - 2; i++)
    {
        cnt = 0;
        while(i - cnt >= 0 && i + cnt <= lenOfA - 1 && A[i - cnt] == A[i + cnt])
        {
            cnt++;
        }
        cnt--;
        //找到較大長度的回文字符串,保存中心字符的位置
        if(MyMax < 2 * cnt + 1)
        {
            MyMax = 2 * cnt + 1;
            *indexMid = i;
        }
    }
    return MyMax;
}
//第二類“12321”:中間是兩個相同的字符。
int  FindLongPaliSubstr_Even(const char A[],int * First)
{
    int i = 0, cnt = 0;//cnt表示前后移動位數
    int MyMax = 0;
    int lenOfA = strlen(A);
    *First = 0;//中間兩個相同字符的第一個字符位置
    for(i = 0; i <= lenOfA - 2; i++)
    {
        if(A[i] == A[i + 1])
        {
            cnt = 1;
            while(i - cnt >= 0 && (i + 1 + cnt) <= lenOfA - 1 && A[i - cnt] == A[i + 1 + cnt])
            {
                cnt++;
            }
            cnt--;
            //找到較大長度的回文字符串,保存中心第一個字符的位置
            if(MyMax < 2 * cnt + 2)
            {
                MyMax = 2 * cnt + 2;
                *First = i;
            }
        }
    }
    return MyMax;
}

int main(void)
{
    char A[] = "adfadfbadfdg12321fagage";
    int indexMid = 0;
    int First = 0;
    int i = 0;
    //兩種類別的最長回文子串的長度
    int MaxOdd = FindLongPaliSubstr_Odd(A, &indexMid);
    int MaxEven = FindLongPaliSubstr_Even(A, &First);

    printf("indexMid = %d\n", indexMid);
    printf("First = %d\n", First);
    //哪類比較大,輸出哪一類的回文子串
    if( MaxOdd > MaxEven)
    {
        for(i = indexMid - (MaxOdd - 1) / 2; i <= indexMid + (MaxOdd - 1) / 2; i++)
        {
            putchar(A[i]);
        }
    }
    else
    {
        for(i = First - (MaxEven - 2) / 2; i <= First + 1 + (MaxEven - 2) / 2; i++)
        {
            putchar(A[i]);
        }
    }
    return 0;
}
View Code

 

3、數軸上從左到右有n各點a[0], a[1], ……,a[n -1],給定一根長度為L的繩子,求繩子最多能覆蓋其中的幾個點。

解:我對第3題的題解也是很常規,就是把相鄰兩個點的距離求出來,保存在一個數組arr[N]里面,從頭到尾遍歷數組arr[N],和直接選擇排序差不多,寫兩個for循環,第一個for循環(外循環)中計數器 i 表示連續線段的起點,第二個for循環(內循環)的計數器 j (i + 1)開始,依次累加Sum,若Sum > L,則記錄點的個數(j - i)中的較大值max;其中,外循環,只要遇到的數比L大,就continue,內循環,只要遇到的數比L大,就break,這是因為長度為L的繩子是不可能覆蓋這些點的,可以直接跳過!

如圖:

my code:

 

/*
    數軸上從左到右有n個點a[0],a[1],...,a[n - 1],給定一根長度為L的繩子,求繩子最多能覆蓋其中幾個點。

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 8
int MaxTimesOfL(int A[], int L)
{
    int i = 0, j = 0;
    int *arr = (int *)malloc(sizeof(int) * (N - 1));
    memset(arr, 0, sizeof(int) * (N - 1));
    //初始化數組arr,兩點間的距離為一個數組元素
    for(i = 0; i < N - 1; i++)
    {
        arr[i] = A[i + 1] - A[i];
    }
    //輸出該數組
    for(i = 0; i < N - 1; i++)
    {
        printf("%-3d", arr[i]);
    }

    int MaxTimes = 0;
    int Sum = 0;
    //遍歷找到覆蓋的最多點數
    for(i = 0; i < N; i++)
    {
        if(arr[i] > L)//遇到比L大的數則跳過
        {
            continue;
        }
        Sum = arr[i];
        for(j = i + 1; j < N - 1; j++)
        {
            if(arr[j] > L)//遇到比L大的數則跳過,這一句對於程序來說加與不加都一樣
            {
                break;
            }
            Sum += arr[j];
            if(Sum > L)
            {
                break;
            }
        }
        MaxTimes = (MaxTimes > (j - i)) ? MaxTimes : (j - i);
    }
    return (MaxTimes + 1);//因為是線段,所以要加1表示覆蓋的點數
}
int main(void)
{
    int A[] = {-1, 0, 3, 9, 11, 13, 14, 25};
    int L = 5;
    int result = MaxTimesOfL(A, L);
    printf("\nthe max times is %d\n", result);
    return 0;
}
View Code

   更新內容:(10月4日下午)

  有網友指出,我的算法其實沒必要申請多余的數組,那么有沒有更加高效的算法呢,我身邊的一個大神給了我一個O(N)復雜度的算法:

  他的原話:兩個指針,一個front,一個rear,每次front-rear,比L小,看覆蓋的點數。保存覆蓋點數的最大值,然后front++;比L大,rear++,每個數最多遍歷2遍,復雜度O(N)。

  對於這個算法,他給了一個形象的比喻:

  就好像一條長度為L的蛇。頭伸不過去的話,就把尾巴縮過來最多只需要走一次,就知道能覆蓋幾個點

實現代碼:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int front = 0 , rear = 0;//設置首尾指針索引
    int cnt = 8;
    int L = 15;//繩子長度
    int MaxTimes = 0;
    int arr[] = {-1, 0, 3, 9, 11, 13, 14, 25};//數軸上的點
    while(front < cnt)
    {
        //比L小,則計算MaxTimes,作front++;
        if(arr[front] - arr[rear] <= L)
        {
            MaxTimes = MaxTimes > (front - rear) ? MaxTimes : (front - rear);
            front++;
        }
        else//比L大,rear++;
        {
            rear++;
        }
    }
    printf("the max times is %d\n", MaxTimes + 1);//第一個數是沒有參與計數的,所以要在最后加1
    return 0;
}
View Code

 

最后貼一下試題:

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM