簡體   English   中英

針對“查找字符串中的第一個重復字符”挑戰的最緊湊,高效,易讀和明智的解決方案

[英]Most compact, efficient, readable and clever solution for the “Find the first repeated character in a string” challenge

我明天要進行編程面試,我正在為此做練習。 我讀到一個常見的問題是使用原型在字符串中找到第一個重復的字符

size_t FindFirstRepeatedChar ( char * );

我能想到的最好的解決方案是

#include <string.h>
#include <vector>

char FindFirstRepeatedChar ( char * S )
{
   /* Returns the index of the first repeated character in the string S. 
      Assume S does indeed contain a repeat.
   */
   std::vector<bool> booVec(128,false)
   for (char * c1(S), * c2(S+strlen(S)); c1 != c2; ++c1)
   {
       size_t i((size_t)*c1); 
       if (booVec[i] == false) booVec[i] = true;
       else return (char)i;
   }   
}

但我想知道

(1)這是錯誤的嗎?

(2)如果沒錯,我有什么方法可以保存電子,即進一步優化電子?

(3)有沒有一種我可以利用的標准庫算法可以在1行中求解?

通常,面試並不期望您說這個庫已經做到了,因為他們想檢查您是否可以編碼和編寫算法。

為了改進它,我會在一次采訪中說,他們可能不是在要求最佳的優化,而是要求更多易於閱讀的東西(這也意味着在編寫時會減少出錯的風險)。

我可能會在第一個中編寫一個非常簡單的嵌套循環。 它比較慢,但是很容易編寫。 如果他們要我更快地做某事,我將在稍后解決。

您在該程序中可能遇到的主要錯誤是,您輸入了一個char (通常為8位),並且向量僅存儲128個元素,因此,如果字符串中包含非ASCII字符,它將崩潰。

您的代碼有一些缺陷。

首先, char可能是簽名的,也可能是未簽名的,而將signed char直接轉換為size_t 可能會由於符號擴展而產生意想不到的后果 當然,這只是很有趣,如果您接受可能存在大於127的任何字符,而您的代碼當前根本不支持這些字符-如果不是這種情況,則可以進一步減小vector的大小。

其次,執行兩次迭代,其中一次命中整個字符串: strlen只能通過遍歷整個字符串來執行其工作...我們可以在此處添加一些booVec[0] ,方法是將booVec[0]的初始值設置為true :這將在第一次出現時觸發return ,從而使我們可以完全消除循環退出條件,同時提高安全性。

第三,您的代碼不支持unicode,盡管這可能是設計使然。

通過用char* char const*替換char* ,我們還允許代碼也可以處理例如字符串文字。

最后,您可以將vector的用法替換為數組。 這將稍微增加運行時內存的使用量,但可能會顯着提高性能(由於不需要堆分配,也無需進行位操作以從位向量中提取位)。

char FindFirstRepeatedChar(char const* S)
{
    bool booVec[(std::numeric_limits<char>::max)()] = { true };
    for (char const* c(S); ; ++c)
    {
        size_t i(static_cast<unsigned char>(*c)); 
        if (booVec[i] == false) booVec[i] = true;
        else return *c;
    }
}

最后,對於標准庫:由於標准庫算法需要使用最終迭代器,因此它們的性能會降低,而不會增加額外的麻煩(例如,編寫具有有趣語義的迭代器類型類型),這比整個示例需要更多的代碼。

我正在考慮使用正則表達式匹配的方法。 假設有重復項,這可以工作。 perl -pe '$_=reverse;s/(.*(.).*\\2.*)/\\2/s' 但是,如果我能擺脫相反的情況,那就更好了。

我可以試試這個:

char FindFirstRepeatedChar(char *S) {
    size_t i = 1;
    while (!memchr(S, S[i], i)) i++;
    return S[i];
}

重復必須出現在前256個字符中(通過信鴿原理),因此此代碼始終在O(1)時間運行。

問題:

  • 其他人已經觀察到的:負字符問題最嚴重,因為它將崩潰。 使用向量效率低下,因為它動態分配內存,這通常是昂貴的操作。 通過動態測試空字符可以避免只為查找字符串結尾而對字符串進行一次迭代。

  • 我主要擔心的是,您從要求/指定的內容中默默地更改了函數的返回類型。 在這種情況下,這非常糟糕,因為僅閱讀規范/文檔的用戶將假定FindFirstRepeatedChar返回size_t。 他可以編碼

     size_t firstRepIndex = S[FindFirstRepeatedChar ( char * S )]; 

    這甚至不會導致警告,因為char具有到size_t的整數升級路徑。 如果S短,則該“索引”(在1..255范圍內)可能超出范圍,因此與之索引的S將為UB。

    返回char所傳達的信息也少於索引(重復在哪里?)。 最后,如果某天您可能想測試未知字符串,則索引的值為“無重復”(0或最后一個元素索引的最后一個)。 字符沒有“轉義值”,因為在一般情況下字符可以容納任何位模式,包括0(盡管在這種情況下,由於不存在長度參數,因此我們必須假定0終止的字符串參數,以便可以使用0表示“未找到”)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM