leetcode205周赛

周赛链接:https://leetcode-cn.com/contest/weekly-contest-205/

T1.替换所有的问号
给你一个仅包含小写英文字母和 ‘?’ 字符的字符串 s ,请你将所有的 ‘?’ 转换为若干小写字母,使最终的字符串不包含任何 连续重复 的字符。

注意:你 不能 修改非 ‘?’ 字符。

题目测试用例保证 除 ‘?’ 字符 之外,不存在连续重复的字符。

在完成所有转换(可能无需转换)后返回最终的字符串。如果有多个解决方案,请返回其中任何一个。可以证明,在给定的约束条件下,答案总是存在的。

示例 1:

输入:s = “?zs”
输出:“azs”
解释:该示例共有 25 种解决方案,从 “azs” 到 “yzs” 都是符合题目要求的。只有 “z” 是无效的修改,因为字符串 “zzs” 中有连续重复的两个 ‘z’ 。

提示:

1 <= s.length <= 100

s 仅包含小写英文字母和 ‘?’ 字符

分析:相邻的字符不同,相邻字符包括左右两边,那么当前字符选择的限制,就来自左边两边的字符,那就简单了,我们只需要选择一个 既不和左边又不和右边相等的小写字母。
思路:如何进行选择呢?
1.首先我们处理特殊情况,
(1)如果前一个字符/或者后一个字符,不存在(越界),我们设为‘#’(只需要设置为小写字母之外的字符即可)
(2)如果后一个字符为‘?’,我们同样设置为‘#’

2.处理好特殊情况后,我们很容易得到 当前字符的左右相邻的字符

3.从a~z中选择一个 既不等于左边字符 又不等于右边字符 的小写字母即可

4.当然 直接在首尾预先插入‘#’,就不同特判越界的情况(本代码未添加,比赛直接写了,没想这么多)
代码:

class Solution
{
public:
    char fd(char p, char s)//选择一个合适的小写字母
    {
        for (char c = 'a'; c <= 'z'; c++)
        {
            if (c != p && c != s)
            {
                return c;
            }
        }
        return '#';
    }
    string modifyString(string s)
    {
        string t = "";
        for (int i = 0; i < s.length(); i++)
        {
            if (s[i] != '?')
            {
                t += s[i];
            }
            else
            {
                char x, y;
                if (i - 1 < 0)
                {
                    x = '#';
                }
                else
                {
                    x = t[i - 1];
                }
                if (i + 1 > s.length() - 1 || s[i + 1] == '?')
                {
                    y = '#';
                }
                else
                {
                    y = s[i + 1];
                }
                char tp = fd(x, y);
                t += tp;
            }
        }
        return t;
    }
};

T2. 数的平方等于两数乘积的方法数

给你两个整数数组 nums1 和 nums2 ,请你返回根据以下规则形成的三元组的数目(类型 1 和类型 2 ):

类型 1:三元组 (i, j, k) ,如果 nums1[i]2 == nums2[j] * nums2[k] 其中 0 <= i < nums1.length 且 0 <= j < k < nums2.length
类型 2:三元组 (i, j, k) ,如果 nums2[i]2 == nums1[j] * nums1[k] 其中 0 <= i < nums2.length 且 0 <= j < k < nums1.length

分析:
就是一个数组里的值,等于另外一个数组里两个元素的积

思路:
很直接,
1.预处理两个数组里所有不同元素的积
2.遍历每个数组,查询即可
注意!
数值范围爆int,要用long long

代码:

class Solution
{
public:
    typedef long ll;
    map<ll, ll> m1, m2;

    int numTriplets(vector<int> &nums1, vector<int> &nums2)
    {
        for (int i = 0; i < nums1.size(); i++)
        {
            for (int j = i + 1; j < nums1.size(); j++)
            {
                m1[(ll)nums1[i] * (ll)nums1[j]]++;
            }
        }
        for (int i = 0; i < nums2.size(); i++)
        {
            for (int j = i + 1; j < nums2.size(); j++)
            {
                m2[(ll)nums2[i] * (ll)nums2[j]]++;
            }
        }
        int sum = 0;
        for (int i = 0; i < nums1.size(); i++)
        {
            sum += m2[(ll)nums1[i] * (ll)nums1[i]];
        }
        for (int i = 0; i < nums2.size(); i++)
        {
            sum += m1[(ll)nums2[i] * (ll)nums2[i]];
        }

        return sum;
    }
};

T3.避免重复字母的最小删除成本
给你一个字符串 s 和一个整数数组 cost ,其中 cost[i] 是从 s 中删除字符 i 的代价。

返回使字符串任意相邻两个字母不相同的最小删除成本。

请注意,删除一个字符后,删除其他字符的成本不会改变。
示例 1:

输入:s = “abaac”, cost = [1,2,3,4,5]
输出:3
提示:

s.length == cost.length
1 <= s.length, cost.length <= 10^5
1 <= cost[i] <= 10^4
s 中只含有小写英文字母

分析:
看完题目,应该比较好分析
1.要使得相邻字母不同,那么我们要 对连续且字母相同的一段区间 进行处理了
2.怎么处理,我们需要将它删除得只剩下一个字符,如何删除使得成本最小
3.当然是保留这一段中cost最大的,剩余的全删除了
4.统计删除掉的cost总和,即答案

思路:
1.用栈保存字符相等且连续的一段区间。我们用sum记录区间cost和,用mx记录区间cost最大值
2.进栈时,
(1)如果栈顶元素与当前元素相等,则进栈,更新sum和mx
(2)若不同,清空栈,计算前一段连续区间的答案,然后在进栈,更新sum和mx
没啥坑,就是注意区间长度大于1才要计算答案

代码:

class Solution
{
public:
    int minCost(string s, vector<int> &cost)
    {
        stack<char> st;
        int sum, mx;
        int res = 0;
        sum = 0;
        mx = 0;
        for (int i = 0; i < s.length(); i++)
        {
            if (st.empty())
            {
                st.push(s[i]);
                sum = cost[i];
                mx = cost[i];
            }
            else
            {
                if (st.top() == s[i])
                {
                    st.push(s[i]);
                    sum += cost[i];
                    mx = max(mx, cost[i]);
                }
                else
                {
                    if (st.size() > 1)
                    {
                        res += (sum - mx);
                        //cout<<res<<endl;
                        // cout<<i<<endl;
                    }
                    while (!st.empty())
                    {
                        st.pop();
                    }
                    st.push(s[i]);

                    sum = mx = cost[i];
                }
            }
        }
        if (!st.empty() && st.size() > 1)
        {
            res += (sum - mx);
        }
        return res;
    }
};

T4.保证图可完全遍历
Alice 和 Bob 共有一个无向图,其中包含 n 个节点和 3 种类型的边:

类型 1:只能由 Alice 遍历。
类型 2:只能由 Bob 遍历。
类型 3:Alice 和 Bob 都可以遍历。
给你一个数组 edges ,其中 edges[i] = [typei, ui, vi] 表示节点 ui 和 vi 之间存在类型为 typei 的双向边。请你在保证图仍能够被 Alice和 Bob 完全遍历的前提下,找出可以删除的最大边数。如果从任何节点开始,Alice 和 Bob 都可以到达所有其他节点,则认为图是可以完全遍历的。

返回可以删除的最大边数,如果 Alice 和 Bob 无法完全遍历图,则返回 -1 。

链接:https://leetcode-cn.com/problems/remove-max-number-of-edges-to-keep-graph-fully-traversable

分析:
这个3个图叠加在一起的问题,我们分开考虑/分析
1.首先问题是 使图联通的情况下,可以删除的最大边数,好像不太熟悉,那转换一下,->使图联通的情况下,可以添加的最小边数!!!介不是MST(最小生成树)吗!
2.如果有公共边,我们肯定用公共边对不对?因为它一条边顶两条边用啊,保证添加最少边的性质
公共边组成的子图里怎么可以最简(边最少)呢?那就是最小生成树啊!!!
3.那么对于 Alice和 Bob 的图怎么处理,使得添加的边最少呢??
因为公共边已经处理(MST,最小生成树)好了,在公共边的基础上,如何添加边,让 Alice和 Bob 都能遍历
分别对 Alice的边和Bob 的边做MST,在公共边的基础上,分别对ALice和Bob的边集合,用kurskal添加边即可。

思路:
1.对 type3的边 单独做最小生树
2.1中最小生成树的基础上,分别type
1和type==2的边做最小生成树
3.经过1和 2,我们可以得到 添加最小边数使得 图“可完全遍历”的边数 ,用总边数减去即是答案
注意:用 并查集 判断 不能 保证图可完全遍历的情况

代码:

class Solution
{
public:
    int fa[100005];

    int fd(int x)
    {
        if (fa[x] != x)
        {
            fa[x] = fd(fa[x]);
        }
        else
        {
            return fa[x];
        }
        return fa[x];
    }
    void un(int x, int y)
    {
        int a, b;
        a = fd(x);
        b = fd(y);
        if (a != b)
        {
            fa[a] = b;
        }
    }
    void init()
    {
        for (int i = 1; i < 100005; i++)
            fa[i] = i;
    }
    bool ck(int n)
    {
        map<int, int> mp;
        //cout<<n<<endl;
        for (int i = 1; i <= n; i++)
        {
            //cout<<fd(i)<<" ";
            mp[fd(i)]++;
        }
        // cout<<mp.size()<<endl;
        if (mp.size() > 1)
            return 0;
        return 1;
    }
    int maxNumEdgesToRemove(int n, vector<vector<int>> &edges)
    {
        int e3 = 0;

        int e2, e1;
        e2 = e1 = 0;
        int res = 0;
        init();
        for (auto v : edges)
        {
            if (v[0] == 3)
            {
                if (fd(v[1]) != fd(v[2]))
                {
                    un(v[1], v[2]);
                    e3++;
                    res++;
                }
            }
        }
        for (auto v : edges)
        {
            if (v[0] == 1)
            {
                if (fd(v[1]) != fd(v[2]))
                {
                    un(v[1], v[2]);
                    e1++;
                    res++;
                }
            }
        }
        if (!ck(n))
            return -1;
        init();
        for (auto v : edges)
        {
            if (v[0] == 3)
            {
                if (fd(v[1]) != fd(v[2]))
                {
                    un(v[1], v[2]);
                    e3++;
                    //res++;
                }
            }
        }
        for (auto v : edges)
        {
            if (v[0] == 2)
            {
                if (fd(v[1]) != fd(v[2]))
                {
                    un(v[1], v[2]);
                    e1++;
                    res++;
                }
            }
        }
        if (!ck(n))
            return -1;

        return edges.size() - res;
    }
};

总结:总体难度偏小,需要一点分析能力

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值