Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
2022-03-19每日刷题打卡
代码源——每日一题
子串分值和 - 题目 - Daimayuan Online Judge
对于一个字符串 S ,我们定义 f(S) 为 S 中出现的不同的字符个数。 例如 f(aba)=2,f(abc)=3,f(aaa)=1。
现在给定一个字符串 S (假设长度为 len),请你计算 len−1∑i=0 len−1∑ j=i f(S[i:j]) 。
输入格式
输入一行包含一个由小写字母组成的字符串 SS 。
输出格式
输出一个整数表示答案。
样例输入
ababc
样例输出
28
数据规模
所有数据保证字符串长度 len≤1000000,字符串下标从 0 到 len−1。
第一眼写法是枚举区间长度然后把每个区间的f[s]算出来并加一起,但这做法是n^2显然超时,所以我们就换个思路。
我们可以计算每个字符的贡献,并计算他们的贡献和。怎么计算贡献呢?因为区间内重复的字符只算一个,所以对于样例ababc中第一个a字母来说,它能被计数的情况就是从上一个字母a开始(因为没有所以实际上是从它开始),区间长度从1到len的len种情况(虽然到第3个位置也有个字符a,但我们只记录一个,所以记录的还是最先出现的a),那么第一个字母a所能提供的贡献一共就是len,即5。我们再看第二个字母a,和前面说的一样,他能被计数的情况,就是从上一个相同的字母开始,枚举区间长度直到到达字符串末尾,所以第二个字母所能提供的贡献就是:(当前字母位置 - 上一个相同字母)*(区间长度 - 当前字母位置)=2 *3=6;我们只要如法炮制,求得所有字符的贡献,并把计算贡献的总和即可。
虽然看似要找上一个相同字母的位置有点麻烦,但实际上我们用一个哈希表或者26长度的数组就可以,专门记录上一个相同字符出现的位置,因为一开始还没有相同的字符所以初始化为-1)
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define endl '\n';
typedef long long ll;
typedef pair<int, int>PII;
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
string str;
cin >> str;
int a[26];
memset(a, -1, sizeof a);
ll res = 0;
int n = str.size();
for (int i = 0; i < n; i++)
{
res += (i - a[str[i]-'a']) * (n - i);
a[str[i] - 'a'] = i;
}
cout << res << endl;
return 0;
}
蜗蜗的数列 - 题目 - Daimayuan Online Judge
题目描述
蜗蜗有两个长度都为 n 的数列 A,B,同时他会进行 q 次操作。
对于每一次操作,他会先选择其中一个数列 (A/B) ,再选择一个区间 [l,r] (1≤l≤r≤n),将选定的序列 [l,r] 中的数对位加上Fibonacci数列。
换句话说,就是将选定数列的第 l 项加上 1,第 l+1 项加上 1,第 l+2 项加上 2,第 l+3 项加上 3… 第 r 项加上 Fib(r−l+1),即 Fibonacci 数列的第 r−l+1 项。
在每次操作结束的时候,蜗蜗都会变得非常好奇。他想知道此时 A 和 B 两个序列是否相同,由于他一看到比较长的数就会头晕,所以你只需要判断 A 和 B 在模 M 的意义下是否相同即可。
输入格式
第一行三个数 n,q,M,分别表示数列的长度,操作的总次数和模数。
第二行和第三行各输入 n 个整数,表示 A 和 B 的初始值。
接下来 q 行每行包含一个字符 c 和两个整数 l,r,描述一次操作。具体细节见样例。
输出格式
输出 q 行,每行一个字符串 Yes 或 No ,表示此时两个数列是否在模 M 的意义下相同。
样例1输入
3 5 3
2 2 1
0 0 0
A 1 3
A 1 3
B 1 1
B 2 2
A 3 3
样例1输出
Yes
No
No
No
Yes
看到这种给一个数组中的一段连续加上值后判断值的情况的,一开始想到的是线段树(但是我不会啊www),在想着要不要先去学个线段树再回来写这题时,去cf上看到了用差分的方式做,但这有两个数组怎么差分?。
首先,我们要知道的是a和b是否相等,而我们知道如果a==b,那么a-b=0,我们可以构造一个数组c,c[i]=a[i]-b[i],这样如果c数组内部全是0,就说明a数组和b数组是相等的。当对a数组进行加上斐波那契数列的操作时,就相当于给c数组加上了斐波那契数列;反之对b加上fib,则c是减去fib。这样,我们就可以在c数组上进行操作了。
此时我们就可以用差分的方式来维护c数组的值了,再创建一个数组d。关于d[i]的值的情况,我们要先说明下差分的性质:区间加同一个数可以差分是因为增量序列的递推式为 ai=ai-1,而后一个数减去前一个数正好抵消。
此时我们加上的不是相同的数,而是斐波那契数列怎么办呢?注意,fib的递推式为fib(i)=fib(i-1)+fib(i-2)。那么我们用一个数(位置从三开始)减去它前面的两个数,也是可以正好抵消的,所以我们d数组应该为d[i]=c[i]-c[i-1]-c[i-2]。d[1]=c[1],d[2]=c[2]-c[1]。正常差分是对区间左端点加上值,并给区间右端点+1减去值,所以此时的d数组应该是每次给左端点加上1,右端点+1处减去fib[r-l+2],右端点+2处减去fib[r-l+1]。所以可以看出我们的d区间应该比ab区间的长度多2。
然后我们就处理d区间就可以了,一开始计算1到n位置的数有多少个0(额外扩建的两个位置不用算)。然后每次对数组A[l,r]操作时,d[l]-=1,d[r+1]-=fib[r-l+2],d[r+2]-=fib[r-l+1],如果是B数组则加减要反过来。然后计算下0数字的改变情况,如果0的数量等于n,说明c都是0,说明A和B相等。
(这题真是要了我老命了,佬们帮帮忙给战队点个赞吧球球了,每个人每天都可以点的)
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define endl '\n';
typedef long long ll;
typedef pair<int, int>PII;
const int N = 1e6 + 50;
ll a[N], b[N], c[N], d[N], fib[N];
ll res = 0, n, m, MOD;
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n >> m >> MOD;
fib[1] = 1, fib[2] = 1;
for (int i = 3; i <= 1e6+5; i++)fib[i] = (fib[i - 1] + fib[i - 2]) % MOD;
for (int i = 1; i <= n; i++)cin >> a[i];
for (int i = 1; i <= n; i++)
{
cin >> b[i];
c[i] = a[i] - b[i];
}
d[1] = c[1];
d[2] = (c[2] - c[1])%MOD;
for (int i = 1; i <= n + 2; i++)
{
if(i>=3)d[i] = (c[i] - c[i - 1] - c[i - 2])%MOD;
if (i<=n&&d[i] == 0)res++;
}
while (m--)
{
char st;
int l, r;
cin >> st >> l >> r;
if (st == 'A')
{
if (d[l] == 0)res--;
if (r + 1 <= n && d[r + 1] == 0)res--;
if (r + 2 <= n && d[r + 2] == 0)res--;
d[l] = (d[l] + 1) % MOD;
d[r + 1] = (d[r + 1] - fib[r - l + 2]) % MOD;
d[r + 2] = (d[r + 2] - fib[r - l + 1]) % MOD;
if (d[l] == 0)res++;
if (r + 1 <= n && d[r + 1] == 0)res++;
if (r + 1 <= n && d[r + 2] == 0)res++;
}
else
{
if (d[l] == 0)res--;
if (r+1<=n&&d[r + 1] == 0)res--;
if (r + 2 <= n && d[r + 2] == 0)res--;
d[l] = (d[l] - 1) % MOD;
d[r + 1] = (d[r + 1] + fib[r - l + 2]) % MOD;
d[r + 2] = (d[r + 2] + fib[r - l + 1]) % MOD;
if (d[l] == 0)res++;
if (r + 1 <= n && d[r + 1] == 0)res++;
if (r + 2 <= n && d[r + 2] == 0)res++;
}
if (res == n)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
}
return 0;
}
力扣
面试题 17.19. 消失的两个数字
给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?
以任意顺序返回这两个数字均可。
示例 1:
输入: [1]
输出: [2,3]
我们记录出现过的数,然后看哪两个数没出现过就行,但这题他要求只能O1的空间,这就说明了不能自己创一个哈希表,这题其实就是原地哈希的知识点。我们用他给的数组做成一个哈希表就行。
因为数组缺了两个数,所以我们先给数组尾插两个数进去,只要是正数就行其他无所谓。然后遍历数组(不要遍历后面的两个位置),把遍历到的值当作下标在数组里找对应的位置,把那个位置的值变成负数,比如我遍历第一个元素是2,我们就把下标为2-1的元素变成负数。就这样遍历一遍过去,然后再遍历一遍,如果哪个位置的值不是负数,说明这个位置的数没有出现在数组里,我们就根据下标把值存起来,最后返回即可。
(虽然是hard,但其实并不难)
class Solution {
public:
vector<int> missingTwo(vector<int>& nums) {
vector<int>v;
int n = nums.size(), ans = 1;
nums.push_back(1);
nums.push_back(1);
for (int i = 0; i < n; i++)
{
nums[abs(nums[i]) - 1] *= -1;
}
for (int i = 0; i < n + 2; i++)
{
if (nums[i] >= 0)
{
v.push_back(ans++);
}
else
{
ans++;
}
}
return v;
}
};