簡體   English   中英

從字符串中刪除指定的字符-高效的方法(時間和空間復雜度)

[英]Removing specified characters from a string - Efficient methods (time and space complexity)

這是問題所在:從給定的字符串中刪除指定的字符。

Input: The string is "Hello World!" and characters to be deleted are "lor"
Output: "He Wd!"

解決這個問題涉及兩個子部分:

  1. 確定是否要刪除給定字符
  2. 如果是這樣,則刪除字符

為了解決第一部分,我正在讀取要刪除的字符到std::unordered_map ,即,我解析字符串“ lor”並將每個字符插入到哈希圖中。 稍后,當我解析主字符串時,我將使用每個字符作為鍵查看此哈希圖,如果返回的值非零,則將從字符串中刪除該字符。

問題1:這是最好的方法嗎?

問題2:哪個對這個問題更好? std::mapstd::unordered_map嗎? 由於我對訂購不感興趣,因此我使用了unordered_map 但是創建哈希表是否有更高的開銷? 在這種情況下該怎么辦? 使用map (平衡樹)還是unordered_map (哈希表)?

現在進入下一部分,即從字符串中刪除字符。 一種方法是刪除字符並將數據從該點開始移回一個位置。 在最壞的情況下,我們必須刪除所有字符,這將花費O(n ^ 2)。

第二種方法是僅將所需的字符復制到另一個緩沖區。 這將涉及分配足夠的內存來容納原始字符串,並逐個字符地進行復制,而忽略要刪除的字符串。 盡管這需要額外的內存,但這將是O(n)操作。

第三種方法是從第0個位置開始讀取和寫入,每次讀取時增加源指針,僅在寫入時增加目標指針。 由於源指針將始終與目標指針相同或位於目標指針之前,因此我可以在同一緩沖區上進行寫操作。 這樣可以節省內存,並且也是O(n)操作。 我在做同樣的事情,並在最后調用resize來刪除其他不必要的字符?

這是我編寫的函數:

// str contains the string (Hello World!)
// chars contains the characters to be deleted (lor)
void remove_chars(string& str, const string& chars)
{
    unordered_map<char, int> chars_map;

    for(string::size_type i = 0; i < chars.size(); ++i)
        chars_map[chars[i]] = 1;

    string::size_type i = 0; // source
    string::size_type j = 0; // destination
    while(i < str.size())
    {
        if(chars_map[str[i]] != 0)
            ++i;
        else
        {
            str[j] = str[i];
            ++i;
            ++j;
        }
    }

    str.resize(j);
}

問題3:我可以通過哪些不同方式來改善此功能。 還是我們能做到的最好?

謝謝!

做得好,現在了解標准庫算法並提高:

str.erase(std::remove_if(str.begin(), str.end(), boost::is_any_of("lor")), str.end());

假設您正在研究算法,並且對庫解決方案不感興趣:

當可能的密鑰數量很大時,哈希表最有價值,但是您只需要存儲其中的幾個即可。 如果要從數字序列中刪除特定的32位整數,則哈希表將很有意義。 但是,對於ASCII字符,這是太過分了。

只需制作一個256個布爾數組,並為要刪除的字符設置一個標志。 每個輸入字符僅使用一個查表指令。 哈希映射至少涉及一些其他指令來計算哈希函數。 在空間上,一旦將所有輔助數據加起來,它們可能不再緊湊。

void remove_chars(string& str, const string& chars)
{
    // set up the look-up table
    std::vector<bool> discard(256, false);
    for (int i = 0; i < chars.size(); ++i)
    {
        discard[chars[i]] = true;
    }

    for (int j = 0; j < str.size(); ++j)
    {
        if (discard[str[j]])
        {
            // do something, depending on your storage choice
        }
    }
}

關於存儲選項:根據是否需要保留輸入數據,在選項2和3之間進行選擇。 3顯然是最有效的,但是您並不總是需要就地過程。

這是具有許多優勢的KISS解決方案:

void remove_chars (char *dest, const char *src, const char *excludes)
{
    do {
        if (!strchr (excludes, *src))
            *dest++ = *src;
    } while (*src++);
    *dest = '\000';
}

您可以在strcspnstrspn之間strcspn乒乓strcspn ,以避免需要哈希表:

void remove_chars(
    const char *input, 
    char *output, 
    const char *characters)
{
    const char *next_input= input;
    char *next_output= output;

    while (*next_input!='\0')
    {
        int copy_length= strspn(next_input, characters);
        memcpy(next_output, next_input, copy_length);

        next_output+= copy_length;

        next_input+= copy_length;
        next_input+= strcspn(next_input, characters);
    }
}

暫無
暫無

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

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