LeetCode291周赛题解 '字符串总引力’详解直接跳到最后
第一题:移除指定数字得到的最大结果
可以直接使用字符串比较(题目指明字符串只包含数字),枚举移除后的字符串,直接比较维护最大值
class Solution {
public:
string removeDigit(string s, char d) {
int n = s.size();
string ans ="0";
for(int i = 0; i < n; i++)
{
string t = "";
if(s[i] == d)
{
t += s.substr(0, i - 0);
t += s.substr(i + 1, n - i);
}
ans = max(ans, t);
}
return ans;
}
};
第二题:必须拿起的最小连续卡牌数
hash维护相同牌的下标,找到距离最短的两个相同牌的长度即为答案,剪枝没有相同牌时返回-1
class Solution {
public:
int minimumCardPickup(vector<int>& c) {
unordered_map<int,int> mp;
int n = c.size();
int ans = INT_MAX;
bool f = false;
for(int i = 0; i < n; i++)
{
if(mp.find(c[i]) == mp.end())
{
mp[c[i]] = i;
}
else
{
ans = min(ans, i - mp[c[i]] + 1);
mp[c[i]] = i;
f = true;
}
}
if(!f)
return -1;
return ans;
}
};
第三题:含最多 K 个可整除元素的子数组
数组长度200, 直接枚举所有组合,再利用SET的唯一性,来保证不重复。再按照题意,处理大于K的情况(博主这里使用set<string>
是偷懒之举,建议使用set<vector<int>>
,并重开vector<int>
来维护)
class Solution {
public:
int countDistinct(vector<int>& nums, int k, int p) {
int n = nums.size();
set<string> s;
for(int i = 0; i < n; i++)
{
string str = "";
int t = 0;
for(int j = i; j < n; j++)
{
str += to_string(nums[j]);
str += ',';
if(nums[j] % p == 0)
t++;
if(t > k)
break;
s.insert(str);
}
}
return s.size();
}
};
第四题:字符串总引力 -详解
第四题:字符串总引力
这里放上灵佬的题解附链接灵茶山艾府题解
提示 1-1
将所有子串按照其末尾字符的下标分组。
提示 1-2
考虑两组相邻的子串:以
s
[
i
−
1
]
s[i−1]
s[i−1]结尾的子串、以
s
[
i
]
s[i]
s[i]结尾的子串。
提示 1-3
以
s
[
i
]
s[i]
s[i] 结尾的子串,可以看成是以
s
[
i
−
1
]
s[i−1]
s[i−1] 结尾的子串,在末尾添加上 s[i]组成。
上面这一串提示是思考子串统计类问题的通用技巧之一。
提示 2-1
从左往右遍历
s
s
s,考虑将
s
[
i
]
s[i]
s[i] 添加到以
s
[
i
−
1
]
s[i−1]
s[i−1] 结尾的子串的末尾。添加后,这些子串的引力值会增加多少?
提示 2-2
分类讨论:
-
如果 s [ i ] s[i] s[i] 之前没有遇到过,那么这些子串的引力值都会增加 1 1 1,这些子串的引力值之和会增加 i i i,再加上 1 1 1,即 s [ i ] s[i] s[i] 单独组成的子串的引力值;
-
如果 s [ i ] s[i] s[i] 之前遇到过,设其上次出现的下标为 j j j,那么向子串 s [ 0.. i − 1 ] , s [ 1.. i − 1 ] , s [ 2.. i − 1 ] , ⋯ , s [ j . . i − 1 ] s[0..i−1], s[1..i−1], s[2..i−1],⋯,s[j..i−1] s[0..i−1],s[1..i−1],s[2..i−1],⋯,s[j..i−1]的末尾添加 s [ i ] s[i] s[i] 后,这些子串的引力值是不会变化的,因为 s [ i ] s[i] s[i] 已经在 s [ j ] s[j] s[j] 处出现过了;而子 s [ j + 1.. i − 1 ] , s [ j + 2.. i − 1 ] , ⋯ , s [ i − 1.. i − 1 ] s[j+1..i−1], s[j+2..i−1],⋯,s[i−1..i−1] s[j+1..i−1],s[j+2..i−1],⋯,s[i−1..i−1] 由于不包含字符 s [ i ] s[i] s[i],这些子串的引力值都会增加 1 1 1,因此有 i − j − 1 i−j−1 i−j−1 个子串的引力值会增加 1 1 1,这些子串的引力值之和会增加 i − j − 1 i−j−1 i−j−1,再加上 1 1 1,即 s [ i ] s[i] s[i] 单独组成的子串的引力值。
模拟上述过程,遍历 s s s 的过程中用一个变量 s u m G sumG sumG 维护以 s [ i ] s[i] s[i] 结尾的子串的引力值之和,同时用一个数组 p o s pos pos 记录每个字符最近一次出现的下标。
累加遍历中的 s u m G sumG sumG 即为答案。
这里附上我的理解图(讨论样例一,包含特殊情况,abbca):
结合灵佬的题解:
- 我们在遍历字符串的时候,就是在向前面已经组成的子串后面添加字符(蓝色方框):如刚开始只有 a a a,遍历到 b b b 时,则有了 a b 、 b ab、b ab、b,遍历到第二个 b b b 时,有了 a b b , b b , b . abb,bb,b. abb,bb,b.(可理解为,斜对角包括蓝色,就是遍历当前字符之后,产生的子串)
- 找到子串的规律之后,我们就是来处理“引力数”的问题。根据我们前边的分析,某一次遍历(某一个斜对角)的引力数肯定会增加,这取决于遍历的字符是否出现过
- 如果没有出现过,则当前字符贡献引力数为斜对角的层数,又因为 i i i 是从 0 0 0 开始的,所以归纳为 i + 1 i + 1 i+1
- 如果出现过,如最后一层 a a a,它却取决于前面层 c c c产生的子串,受影响的是
上一次 a 出现的位置一列和之前列
(读者仔细理解这句话),以为这个罗列的是一个正三角,所以影响的引力数也可以用i来表示,所以我们想到了更新出现字符的下标,就有了当前层的引力数 i − j − 1 + 1 = i − j i - j - 1 + 1 = i - j i−j−1+1=i−j至此,所有的情况均分析完,最后的结果,我们可以一直累加当前斜对角层的引力数,当前斜对角层的引力数又等于上一层的加上当前层字符贡献的。代码如下:
class Solution {
public:
long long appealSum(string &s) {
long ans = 0L, sum_g = 0L;
vector<int> pos(26, -1);// -1初始化,刚好处理了,字符没出现过 i + 1
for (int i = 0; i < s.length(); ++i) {
char c = s[i] - 'a';
sum_g += i - pos[c]; //上一斜对角层 + 这一层字符贡献
ans += sum_g; //总的 累加 这一斜对角层
pos[c] = i;//维护字符位置
}
return ans;
}
};