----武漢站,9.28號百度校招筆試題目演算法題目部分
(題解非官方,僅供參考,轉載請聯絡博主,有錯誤的地方望指正!謝謝)
給定任意一個正整數,求比這個數大且最小的“不重複數”,“不重複數”的含義是相鄰兩位不相同,例如1101是重複數,而1201是不重複數。
這道題目我也沒有什麼特別出彩的演算法,就是按照常規思路來求解,首先要理解什麼叫做“不重複數”,這是解題的關鍵之一,相鄰兩位不相同的數即為“不重複數”;還有一個地方要注意的就是“求比這個數大且最小的”,就是在比給定數大的不重複數中找到最小的!理解了這兩個地方,這道題目就可以著手求解了!
因為可以直接定位到“重複”的數位的位置,比如12344,我們可以直接定位到個位元4和十位元4上面,然後在低數假設是對整數12199呢?”;
這裡又產生一個問題,因為“重複“的數位可能不止一對,有可能有多對,比如整數11233,定位“33”之後變為“34”,定位“11”之後變為“12”,結果就是12234。又有重複的地方,就是“22”。繼續拿到迴圈去重複;
還有要注意的是,你的程式的設計,是如何儲存數位的值的,是一個一個數位的值求出來呢?還是像我這樣設定一個前後索引?把每個數位的值求出來並且儲存,程式會變得繁瑣複雜,不易讀懂,如果是設定前後索引,則要注意程式退出迴圈的條件!
數位“重複”的定位和變換,是從高位到低位,還是從低位到高位呢?正確的應該是從高位到低位,這給我們的程式設計也帶來了不便。
/* 給定任意一個正整數,求比這個數大且最小的“不重複數”,“不重複數的含義是相鄰兩位不相同,例如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;}
簡單加1的演算法,效率太低,看到這麼多的朋友的評論,大家的演算法大同小異,我也寫了一個演算法,拿出來和大夥分享。
演算法:
1、把整數放到字元數組裡面去,從高位為低位(用變數i)掃描,找到重複的數位,重複數位為“99”跳到第2步,否則跳到第3步,若沒有重複的數位,則該數為不重複數,返回;
2、遇到“99”的重複數,則把“99”改為“00”,然後在“99”前面一位字元加1,把掃描的地方定位在“99”往高位方向的第2位,比如是1299,變換後為1300,然後把掃描變數
3、遇到非“99”的重複數,則直接在低位加1,後面依次變為010101……,結果就是最小的不重複數,返回改值;
至於前面說的一些痛點,真是害怕誤導了大家,畢竟總有考慮不到的地方,希望見諒!
#include <stdio.h>#include <stdlib.h>#include <string.h>#define SIZE 100int 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 = 119998989; char NumStr[SIZE]; memset(NumStr, '0', SIZE * sizeof(char)); getNumNonrepetition(NumGived, NumStr); return 0;}
題目指出“N很大”,就是提示我們不要想通過遍曆的方法來找到這個字串,我想到的就一種解法,時間複雜度應該不高,但是我算不出來這個演算法的複雜度是多少,首先說一下什麼是迴文字串:迴文字串是指從左至右和從右至左相同的字串,比如"1221"或者“12321”都是迴文字串。剛好舉得這兩個迴文字串的例子就是我的演算法的兩個類別:
演算法的思想是從第2個字元直到倒數第2個字元遍曆,每遇到一個字元,就依次判斷這個字元前後的字元是否相等,如果相等,則繼續判斷下一個字元,直到以這個字元為中心的兩邊對稱的字元不相等為止,或者前後字元的位置數組越界為止;計算此時的迴文字串的長度,與之前的比較,記下較長的迴文字串的長度和中心字元的位置;遍曆結束則返回最大長度和中心字元的位置。
演算法思想同上,其實是一樣的過程!圖解也是一樣的!
/* 長度為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;}
表示連續線段的起點,第二個for迴圈(內迴圈)的計數器 j 從 (i + 1)開始,依次累加Sum,若Sum > L,則記錄點的個數(j - i)中的較大值max;其中,外迴圈,只要遇到的數比L大,就continue,內迴圈,只要遇到的數比L大,就break,這是因為長度為L的繩子是不可能覆蓋這些點的,可以直接跳過!
/* 數軸上從左至右有n個點a[0],a[1],...,a[n - 1],給定一根長度為L的繩子,求繩子最多能覆蓋其中幾個點。*/#include <stdio.h>#include <stdlib.h>#include <string.h>#define N 8int 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;}
#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;}