劍指offer 面試題35擴充套件:從第一個字串中刪除第二個字串中的所有字元 (C++版)
題目描述:
例如,輸入”They are students.”和”aeiou”,則刪除之後的第一個字串變成”Thy r stdnts.”。
思路分析:
總體來說,就是在第一字元中拿到一個字元,判斷其是否在第二個字串中,在的話,就刪除該字元。
考慮如下幾個問題:
1、如何在字串中刪除一個字元:
字串的記憶體是連續分配的,當我們刪除其中一個字元時,就需要把後面所有的字元向前移動一個位元組的位置。因此,對於一個長度為n的字串,刪除一個字元的時間複雜度為O(n)。對於本題而言,假設第二個字串的長度為m,有可能要刪除的字元個數是m,因此刪除的時間複雜度為O(m*n),即O(n^2)。
我們換一種思路。採用在原字串基礎上重新構造刪除後的字串的思路。定義兩個指標,一個slow用來構造新的字串,一個fast用來遍歷原字串。初始兩個指標均指向原字串的第一個位置。如果fast指向的字元不是要刪除的字元,就賦值給slow,然後兩個指標一起後移;如果是要刪除的,就跳過該字元,fast繼續遍歷下一個字元,slow不變。這樣,刪除在O(n)時間內就可以搞定。
2、如何在一個字串中查詢一個字元:
我們需要判斷第一個字串中的所有字元是否在第二個字串中存在。最容易想到的辦法是,兩層迴圈遍歷,時間複雜度為O(n^2)。
簡單方法:採用雜湊表的思想:字串的總數是有限的。對於8位的char型字元,共有2^8=256個字元。新建一個256的陣列,把所有元素都初始化為0。對於第二個字串中每一個字元,把它的ascii碼對映為該陣列的索引,把陣列的該索引對應的值設為1。這樣,再查詢第一個字串中的字元是否在第二個字串中就很方便了,根據字元的ascii碼,在陣列對應的下標找到其對應的陣列值,為0表示第二個字串中沒有該字元,為1,表示有,只需要O(1)時間。這樣,查詢長度為n的第一個字串中的字元是否在第二個字串中,時間複雜度降為O(n)。
程式碼及測試:
#include <iostream> #include <assert.h> using namespace std; //在一個字串中刪除某些特定字元 char * deleteChars(char *first, char * second) { assert( (NULL != first) && (NULL != second) ); //標記字元是否在字串second中出現過 const int tableSize = 256; bool hashTable[tableSize]; memset(hashTable, 0, sizeof(hashTable)); while( *second != '\0') { if( !hashTable[ *second ] ) { hashTable[ *second ] = true; } second ++; } //刪除first中所有出現在second中的字元 char *fast = first; char *slow = first; while( *fast != '\0') { if( !hashTable[*fast]){ *slow = *fast; ++ slow; } ++ fast; } *slow = '\0'; return first;//注意這裡應該是first而不是slow,slow此時已經指向first最末尾了 } void test1() { //second中的字元沒有出現在first中的 cout << "*****test1 second中的字元沒有出現在first中的:*****"; char text[] = "abcdbbad"; char *first = text; char second[] = "lmni"; first = deleteChars(first, second); cout << first << endl; } void test2() { //second中的字元有出現在first中的 cout << "*****test1 second中的字元有出現在first中的:*****"; char text[] = "abcdbbad"; char *first = text; char second[] = "adfghb"; first = deleteChars(first, second); cout << first << endl; } int main() { test1(); test2(); return 0; }