力扣刷題——2306. 公司命名

SuzumiyaYui發表於2024-09-26

根據題意,很容易就能想到用雜湊表來做。先寫一個最簡單的暴力,完全就是模擬。

long long distinctNames(vector<string> &ideas)
{
    int n = ideas.size();
    unordered_map<string, int> um;
    for (int i = 0; i < n; i++)
    {
        um[ideas[i]] = 1;
    }
    long long res = 0;
    for (int i = 0; i < n - 1; i++)
    {
        string &a = ideas[i];
        for (int j = i + 1; j < n; j++)
        {
            string &b = ideas[j];
            if (a[0] == b[0])
                continue;

            if (um.find(b[0] + a.substr(1, a.size() - 1)) != um.end() || um.find(a[0] + b.substr(1, b.size() - 1)) != um.end())
                continue;
            res++;
        }
    }
    res *= 2;
    return res;
}

但是不出意外肯定會超時,假如在雜湊表裡存字尾,就能減少構造新string的次數,看看行不行。

long long distinctNames(vector<string> &ideas)
{
    int n = ideas.size();
    unordered_map<string, vector<int>> um;
    for (int i = 0; i < n; i++)
    {
        if (um.find(ideas[i].substr(1, ideas[i].size() - 1)) != um.end())
            um[ideas[i].substr(1, ideas[i].size() - 1)][ideas[i][0] - 'a'] = 1;
        else
        {
            um[ideas[i].substr(1, ideas[i].size() - 1)] = vector<int>(26, 0);
            um[ideas[i].substr(1, ideas[i].size() - 1)][ideas[i][0] - 'a'] = 1;
        }
    }
    long long res = 0;
    for (int i = 0; i < n - 1; i++)
    {
        string &a = ideas[i];
        for (int j = i + 1; j < n; j++)
        {
            string &b = ideas[j];
            if (a[0] == b[0])
                continue;

            if (um[a.substr(1, a.size() - 1)][b[0] - 'a'] || um[b.substr(1, b.size() - 1)][a[0] - 'a'])
                continue;
            res++;
        }
    }
    res *= 2;
    return res;
}

上述的最佳化將雜湊表改造成了存字尾和首字母的一個結構,這樣在原來比對過程中就不需要用過載的+號運算子構造新string,以此提升速度。但是仍然會超時,應該是呼叫雜湊表的次數太多了,並且雜湊表中內容也很多。
再次分析一下問題,當進行判斷是否能夠組成一個新店名的時候,其實可以不用透過呼叫雜湊表來計算。
舉例子說,當ideas = {"coffee", "donuts", "time", "toffee"},依次將字尾加入雜湊表中,同時再維護一個交集陣列和首字母陣列。
在遍歷完donuts時,coffee只能選擇 donuts。換句話說,只有1種選擇,即首字母c和首字母d選擇1次
在遍歷完time時,coffee能選擇 donutstime,同時donuts還能選擇time。這時,就有3種選擇,c能選擇dt各1次,d能選擇t1次
在遍歷完toffee時,出現了不能選的情況,如果不剔除的話,情況應該是:
選擇1:c能選擇t2次
選擇2:c能選擇d1次
選擇3:d能選擇t2次
透過字尾offee在字典中的資訊,可以知道產生了交集。首先,因為coffeetime,導致了ct的選擇存在相同字尾,選擇1減少1次;接著,因為coffeetoffee,導致了ct的選擇存在相同字尾,選擇1減少1次。
在交集陣列的維護過程中,不用考慮相同首字母的情況做特別的維護,因為這個可以在最終計算裡直接避免,因此有實現如下:

long long distinctNames(vector<string> &ideas)
{
    int n = ideas.size();
    vector<int> heads(26, 0);
    vector<vector<int>> intersection(26, vector<int>(26, 0));
    unordered_map<string, vector<int>> um;
    for (int i = 0; i < n; i++)
    {
        int head = ideas[i][0] - 'a';
        heads[head]++;
        if (um.find(ideas[i].substr(1)) != um.end())
        {
            um[ideas[i].substr(1)][head] = 1;
        }
        else
        {
            um[ideas[i].substr(1)] = vector(26, 0);
            um[ideas[i].substr(1)][head] = 1;
        }
        for (int j = 0; j < 26; j++)
        {
            if (um[ideas[i].substr(1)][j] == 1)
            {
                intersection[head][j]++;
                intersection[j][head]++;
            }
        }
    }
    long long res = 0;
    for (int i = 0; i < 25; i++)
    {
        for (int j = i + 1; j < 26; j++)
        {
            res += (long long)(heads[i] - intersection[i][j]) * (heads[j] - intersection[i][j]);
        }
    }
    res *= 2;
    return res;
}

還能做進一步的最佳化,用一個int來表示一個字尾是否存在某個首字母的情況:

long long distinctNames(vector<string> &ideas)
{
    int n = ideas.size();
    vector<int> heads(26, 0);
    vector<vector<int>> intersection(26, vector<int>(26, 0));
    unordered_map<string, int> um;
    for (int i = 0; i < n; i++)
    {
        int head = ideas[i][0] - 'a';
        heads[head]++;
        um[ideas[i].substr(1)] = um[ideas[i].substr(1)] | (1 << head);
        for (int j = 0; j < 26; j++)
        {
            if ((um[ideas[i].substr(1)] >> j) & 1)
            {
                intersection[head][j]++;
                intersection[j][head]++;
            }
        }
    }
    long long res = 0;
    for (int i = 0; i < 25; i++)
    {
        for (int j = i + 1; j < 26; j++)
        {
            res += (long long)(heads[i] - intersection[i][j]) * (heads[j] - intersection[i][j]);
        }
    }
    res *= 2;
    return res;
}

相關文章