2022-03-19每日刷题打卡

2022-03-19每日刷题打卡

力扣春季赛开始啦,佬们求求给战队点个赞吧(每天都可以点的):啊哈哈哈AC来咯

代码源——每日一题

子串分值和 - 题目 - 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 行,每行一个字符串 YesNo ,表示此时两个数列是否在模 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;
}
};
  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值