在字符串中刪除特定的字符


傳說這是一道知名外企的筆試題

但是看了一些文章,都只是單純的轉了那個算法,弱弱的說一句,那個算法中把'\0'寫成了'/0',會導致在

while ('/0' != *pTemp)  
      {  
            hashTable[*pTemp] = 1;  
            ++ pTemp;  
      }  

這一步的時候,一直循環下去,直到系統中斷。不過調試的時候發現了一個很有意思的事情,就是之前pTemp之前是指向aeiou,這就是傳入的第二個參數,當遍歷完這個參數之后,pTemp會繼續指向棧中的下一個地址,也就是第一個參數they are students的地址,這個正好映證了《深入理解計算機系統》上的內容,也說明了這玩意是多么多么的不安全啊。

說了這么多,還是看看具體的這道題目吧。


題目:輸入兩個字符串,從第一字符串中刪除第二個字符串中所有的字符。例如,輸入”They are students.””aeiou”,則刪除之后的第一個字符串變成”Thy r stdnts.”

首 先我們考慮如何在字符串中刪除一個字符。由於字符串的內存分配方式是連續分配的。我們從字符串當中刪除一個字符,需要把后面所有的字符往前移動一個字節的 位置。但如果每次刪除都需要移動字符串后面的字符的話,對於一個長度為n的字符串而言,刪除一個字符的時間復雜度為O(n)。而對於本題而言,有可能要刪 除的字符的個數是n,因此該方法就刪除而言的時間復雜度為O(n^2)。

這個算法我也弱弱的實現了一下,一會兒一起貼出來。

事 實上,我們並不需要在每次刪除一個字符的時候都去移動后面所有的字符。我們可以設想,當一個字符需要被刪除的時候,我們把它所占的位置讓它后面的字符來填 補,也就相當於這個字符被刪除了。在具體實現中,我們可以定義兩個指針(pFast和pSlow),初始的時候都指向第一字符的起始位置。當pFast指 向的字符是需要刪除的字符,則pFast直接跳過,指向下一個字符。如果pFast指向的字符是不需要刪除的字符,那么把pFast指向的字符賦值給 pSlow指向的字符,並且pFast和pStart同時向后移動指向下一個字符。這樣,前面被pFast跳過的字符相當於被刪除了。用這種方法,整個刪 除在O(n)時間內就可以完成。


這一步其實還是蠻不好理解的,或者我太菜了,第一次沒有理解清楚,看了調試的過程,才明白了,這段話是什么意思。這段話對應的就是下面這段代碼

while ('\0' != *pFast)  
{  
      if(1 != hashTable[*pFast])  
      {  
           *pSlow = *pFast;  
            ++ pSlow;  
       }  
       ++pFast;  
}  

初始時,pFast和pSlow都指向 they are students 的第一個字符,之后如果pFast指向的不是要刪除的字符(這是if判斷的內容),則pFast和pSlow一起移動,知道他們都指向e,這時候,不進入if,pFast繼續移動,pSlow還是指向e,當pFast指向y的時候,進入if,這時候,就將pSlow指向的e,替換成了pFast指向的y,也就是這段話的含義。

接下來就是分析,如何更快的確定pFast指向的字符,是在要刪除的字符集中,這里使用了Hash的思想。

接下來我們考慮如何在一個字符串中查找一個字符。當然,最簡單的辦法就是從頭到尾掃描整個字符串。顯然,這種方法需要一個循環,對於一個長度為n的字符串,時間復雜度是O(n)。

由 於字符的總數是有限的。對於八位的char型字符而言,總共只有28=256個字符。我們可以新建一個大小為256的數組,把所有元素都初始化為0。然后 對於字符串中每一個字符,把它的ASCII碼映射成索引,把數組中該索引對應的元素設為1。這個時候,要查找一個字符就變得很快了:根據這個字符的 ASCII碼,在數組中對應的下標找到該元素,如果為0,表示字符串中沒有該字符,否則字符串中包含該字符。此時,查找一個字符的時間復雜度是O(1)。 其實,這個數組就是一個hash表。

完整的代碼如下:

// 從str中,刪除一個指定的字符
void deleteOneChar(char* str, char toBeDelete)
{
    if(str == NULL)  return;
    int length = strlen(str);
    char* p = str;
    // 遍歷str,刪除和toBeDelete相等的字符
    for(int i=0; str[i]!='\0'&&i<length; i++)
    {
        // 如果相等則用后面的元素進行覆蓋
        if(str[i] == toBeDelete)
        {
                int j=i;
                for(; str[j]!='\0'&&j<length-1;j++)
                    str[j]=str[j+1];
                str[j]='\0';
        }
    }
    return;
}

// 使用第一種方法實現 復雜度O(n^2)
void deleteChars1(char* str,const char* chars)
{
    if(str==NULL)  return;
    if(chars == NULL) return;
    // 依次刪除chars中的每一個元素
    for(int i=0; i<strlen(chars); i++)
        deleteOneChar(str,chars[i]);
    return;
}
// 使用第二種方法實現復雜度O(n)
void deleteChars2(char* pStrSource, const char* pStrDelete)  
{  
      if(NULL == pStrSource || NULL == pStrDelete)  
  
            return;  
  
      const unsigned int nTableSize = 256;  
      int hashTable[nTableSize];  
      memset(hashTable, 0, sizeof(hashTable));  
      const char* pTemp = pStrDelete;  
      while ('\0' != *pTemp)  
      {  
            hashTable[*pTemp] = 1;  
            ++ pTemp;  
      }  
      char* pSlow = pStrSource;  
      char* pFast = pStrSource;  
      while ('\0' != *pFast)  
      {  

            if(1 != hashTable[*pFast])  
            {  
                  *pSlow = *pFast;  
                  ++ pSlow;  
            }  
            ++pFast;  
      }  
      *pSlow = '\0';  
  
}  

int main()
{
    char* str = "they are students";
    char* p = (char*)malloc((strlen(str)+1)*sizeof(char));
    strcpy(p,str);
    const char*q = "aeiou";
    deleteChars2(p,q);
    cout<<p<<endl;


    free(p);
    p = NULL;

}

 

 

 

 

 


免責聲明!

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



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