首页 > 其他分享 >力扣刷题——2306. 公司命名

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

时间:2024-09-26 14:13:03浏览次数:8  
标签:um ++ res ideas long 2306 力扣 int 刷题

根据题意,很容易就能想到用哈希表来做。先写一个最简单的暴力,完全就是模拟。

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;
}

标签:um,++,res,ideas,long,2306,力扣,int,刷题
From: https://www.cnblogs.com/yuhixyui/p/18433340

相关文章

  • 【算法题】63. 不同路径 II-力扣(LeetCode)-”如果起点有障碍物,那么便到不了终点“
    【算法题】63.不同路径II-力扣(LeetCode)-”如果起点有障碍物,那么便到不了终点“1.题目下方是力扣官方题目的地址63.不同路径II一个机器人位于一个mxn网格的左上角(起始点在下图中标记为“Start”)。机器人每次只能向下或者向右移动一步。机器人试图达到网格......
  • 9.24刷题记录
    好久没写动归了……1##题目描述在一个n*n的平面上,在每一行中有一条线段,第i$行的线段的左端点是(i,L_{i}),右端点是(i,R_{i})。你从(1,1)点出发,要求沿途走过所有的线段,最终到达(n,n)点,且所走的路程长度要尽量短。更具体一些说,你在任何时候只能选择向下走一步(行数增加......
  • 【算法题】20. 有效的括号-力扣(LeetCode)
    【算法题】20.有效的括号-力扣(LeetCode)1.题目下方是力扣官方题目的地址20.有效的括号给定一个只包括'(',')','{','}','[',']'的字符串s,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对......
  • 【算法题】11. 盛最多水的容器-力扣(LeetCode)
    【算法题】11.盛最多水的容器-力扣(LeetCode)1.题目下方是力扣官方题目的地址11.盛最多水的容器给定一个长度为n的整数数组height。有n条垂线,第i条线的两个端点是(i,0)和(i,height[i])。找出其中的两条线,使得它们与x轴共同构成的容器可以容纳最多的......
  • 【算法题】53. 最大子数组和-力扣(LeetCode)
    【算法题】53.最大子数组和-力扣(LeetCode)1.题目下方是力扣官方题目的地址53.最大子数组和给你一个整数数组nums,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。示例1:输入:nums=[-2,1,-3,4,-1,2,1,-......
  • Day5 JavaWeb知识了解以及每日一题:力扣125.验证回文串
    Day5JavaWeb知识了解以及每日一题:力扣125.验证回文串2024年9月24日20:06:45JavaWeb基础知识TomcatApacheTomcat是一个开源的Servlet容器和Web服务器,它是JavaEE(EnterpriseEdition)的一部分,专门用于运行JavaServlet和JavaServerPages(JSP)。Tomcat的主要功能是接收HTTP......
  • 力扣274.H指数
    题目要求:给你一个整数数组 citations ,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。根据维基百科上 h指数的定义:h 代表“高引用次数”,一名科研人员的 h 指数 是指他(她)至少发表了 h 篇论文,并且 至少 有 h 篇......
  • 力扣题解2207
    大家好,欢迎来到无限大的频道。今日继续给大家带来力扣题解。题目描述(中等)​:字符串中最多数目的子序列给你一个下标从 0 开始的字符串 text 和另一个下标从 0 开始且长度为 2 的字符串 pattern ,两者都只包含小写英文字母。你可以在 text 中任意位置插入 一个......
  • 【刷题笔记】2020 CSP-J
    2020CSP-J题目整理B-直播获奖思路梳理题目中说“如有选手成绩相同,则所有成绩并列的选手都能获奖,因此实际获奖人数可能比计划中多”,这是一个坑点,因为即使有分数相同的人,他的分数也是和位于第\(n*w\%\)人的分数相同的,而题目只让输出分数,所以不用在意。先来考虑暴力算法,没加进......
  • 【刷题笔记】2019 CSP-S
    2019CSP-S题目整理A-格雷数思路简介思路很简单,如果编号在中点的左边那么就输出0,否则输出1,同时还要改变编号。代码实现#include<bits/stdc++.h>#definemaxn80usingnamespacestd;typedef__int128int1;int1n,k;__int128read(){ charch=getchar(); __int128......