2022年2月10日学习总结

千辛万苦终于A对了第一个模板题目

P3375 【模板】KMP字符串匹配

题目描述

给出两个字符串 s_1s1​ 和 s_2s2​,若 s_1s1​ 的区间 [l, r][l,r] 子串与 s_2s2​ 完全相同,则称 s_2s2​ 在 s_1s1​ 中出现了,其出现位置为 ll。
现在请你求出 s_2s2​ 在 s_1s1​ 中所有出现的位置。

定义一个字符串 ss 的 border 为 ss 的一个非 ss 本身的子串 tt,满足 tt 既是 ss 的前缀,又是 ss 的后缀。
对于 s_2s2​,你还需要求出对于其每个前缀 s's′ 的最长 border t't′ 的长度。

输入格式

第一行为一个字符串,即为 s_1s1​。
第二行为一个字符串,即为 s_2s2​。

输出格式

首先输出若干行,每行一个整数,按从小到大的顺序输出 s_2s2​ 在 s_1s1​ 中出现的位置。
最后一行输出 |s_2|∣s2​∣ 个整数,第 ii 个整数表示 s_2s2​ 的长度为 ii 的前缀的最长 border 长度。

输入输出样例

输入 #1复制

ABABABC
ABA

输出 #1复制

1
3
0 0 1 

说明/提示

样例 1 解释

对于 s_2s2​ 长度为 33 的前缀 ABA,字符串 A 既是其后缀也是其前缀,且是最长的,因此最长 border 长度为 11。

数据规模与约定

本题采用多测试点捆绑测试,共有 3 个子任务

  • Subtask 1(30 points):|s_1| \leq 15∣s1​∣≤15,|s_2| \leq 5∣s2​∣≤5。
  • Subtask 2(40 points):|s_1| \leq 10^4∣s1​∣≤104,|s_2| \leq 10^2∣s2​∣≤102。
  • Subtask 3(30 points):无特殊约定。

对于全部的测试点,保证 1 \leq |s_1|,|s_2| \leq 10^61≤∣s1​∣,∣s2​∣≤106,s_1, s_2s1​,s2​ 中均只含大写英文字母。

 解题思路:题解:标准kmp,先将子串的前缀表计算出来,即依次计算出1~n位字符串,最大的前缀等于后缀的位数,但通常当字符串长度为n时的前缀数不要,而是将整个前缀表向后一位,将第一位填成负数,以便之后kmp查找。在计算前缀表时,可以通过数组前一位的前缀数计算后一位的,后一位的前缀数为多少,只需看前一个前缀数的后一个字符与当前位数的字符是否相等,如果相等则当前位数的前缀数加一,否则就是0或小于前一位前缀数的数。有了前缀表就能通过kmp轻松查找子串在主串中重合的开始下标。

AC代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    char a[1000100],b[1000100];
    int p[1000100];
    int main()
    {
        scanf("%s%s",a+1,b+1);
        int la=strlen(a+1),lb=strlen(b+1);
        int j=0;
        p[1]=0;
        for(int i=2;i<=lb;i++)
        {
            while(j>0 && b[i]!=b[j+1]) j=p[j];
            if(b[i]==b[j+1]) j++;
            p[i]=j;
        }
        j=0;
        for(int i=1;i<=la;i++)
        {
            while(j>0 && a[i]!=b[j+1]) j=p[j];
            if(a[i]==b[j+1]) j++;
            if(j==lb) printf("%d\n",i-lb+1),j=p[j];
        }
        for(int i=1;i<lb;i++)
            printf("%d ",p[i]);
        printf("%d",p[lb]);
        return 0;
    }
 

P2580 于是他错误的点名开始了

题目背景

XS中学化学竞赛组教练是一个酷爱炉石的人。

他会一边搓炉石一边点名以至于有一天他连续点到了某个同学两次,然后正好被路过的校长发现了然后就是一顿欧拉欧拉欧拉(详情请见已结束比赛 CON900)。

题目描述

这之后校长任命你为特派探员,每天记录他的点名。校长会提供化学竞赛学生的人数和名单,而你需要告诉校长他有没有点错名。(为什么不直接不让他玩炉石。)

输入格式

第一行一个整数 nn,表示班上人数。

接下来 nn 行,每行一个字符串表示其名字(互不相同,且只含小写字母,长度不超过 5050)。

第 n+2n+2 行一个整数 mm,表示教练报的名字个数。

接下来 mm 行,每行一个字符串表示教练报的名字(只含小写字母,且长度不超过 5050)。

输出格式

对于每个教练报的名字,输出一行。

如果该名字正确且是第一次出现,输出 OK,如果该名字错误,输出 WRONG,如果该名字正确但不是第一次出现,输出 REPEAT

输入输出样例

输入 #1复制

5  
a
b
c
ad
acd
3
a
a
e

输出 #1复制

OK
REPEAT
WRONG

说明/提示

  • 对于 40\%40% 的数据,n\le 1000n≤1000,m\le 2000m≤2000。
  • 对于 70\%70% 的数据,n\le 10^4n≤104,m\le 2\times 10^4m≤2×104。
  • 对于 100\%100% 的数据,n\le 10^4n≤104,m≤10^5m≤105。

解题思路:一开始直接想使用暴力破解但是看了一眼数据范围直接pass掉了。最后用二分查找A出了这个题。

AC代码:

#include<bits/stdc++.h>
using namespace std;
struct student
{
    string name;
    bool a;
};
student ss[10005];
bool operator <(student a,student b) //sort
{
    return a.name<b.name;
}
int fun(int l,int r,char a[51]) //二分查找
{
    int mid=l+(r-l)/2;
    if(ss[mid].name==a)return mid;
    if(l>r)return -1;
    else if(ss[mid].name>a)return fun(l,mid-1,a);
    else return fun(mid+1,r,a);
}
int main()
{
    int n;
    cin>>n;
    for(int i=1; i<=n; i++)
    {
        cin>>ss[i].name;
        ss[i].a=true;
    }
    int m;
    cin>>m;
    sort(ss+1,ss+n+1);
    for(int i=1; i<=m; i++)
    {
        char name[51];
        cin>>name;
        bool x=false;
        int j=fun(1,n,name);
        if(j==-1)  //没找到
        {
            cout<<"WRONG\n";
        }
        else
        {
            if(ss[j].a==false) 
            {
                cout<<"REPEAT\n";
                x=true;
            }
            else if(ss[j].a==true) 
            {
                cout<<"OK\n";
                x=true;
                ss[j].a=false;//第一次点名直接记住
            }
        }
    }
    return 0;
}
 

P1102 A-B 数对

题目描述

出题是一件痛苦的事情!

相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!

好吧,题目是这样的:给出一串数以及一个数字 CC,要求计算出所有 A - B = CA−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。

输入格式

输入共两行。

第一行,两个整数 N, CN,C。

第二行,NN 个整数,作为要求处理的那串数。

输出格式

一行,表示该串数中包含的满足 A - B = CA−B=C 的数对的个数。

输入输出样例

输入 #1复制

4 1
1 1 2 3

输出 #1复制

3

说明/提示

对于 75\%75% 的数据,1 \leq N \leq 20001≤N≤2000。

对于 100\%100% 的数据,1 \leq N \leq 2 \times 10^51≤N≤2×105。

保证所有输入数据绝对值小于 2^{30}230,且 C \ge 1C≥1。

2017/4/29 新添数据两组

解题思路:这个题目非常的有意思,在草稿本上算了一段时候后得出思路:A-B=C也就是B+C=A。果断使用桶排序,但是非常不幸,没有A掉,在寝室群寻求帮助之后,去学了一下map,先用用map映射,每个数加c的映射次数相加就是总数。

AD代码:

#include<bits/stdc++.h>
using namespace std;
map<int,int> a;
int n,c;
long long ans;
int num[200005];
int main()
{
    cin>>n>>c;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
        a[num[i]]++;
    }
    for(int i=1;i<=n;i++)
    {
        ans+=a[num[i]+c];
    }
    printf("%lld",ans);
    return 0;
}
 

P3370 【模板】字符串哈希

题目描述

如题,给定 NN 个字符串(第 ii 个字符串长度为 M_iMi​,字符串内包含数字、大小写字母,大小写敏感),请求出 NN 个字符串中共有多少个不同的字符串。

友情提醒:如果真的想好好练习哈希的话,请自觉,否则请右转PJ试炼场:)

输入格式

第一行包含一个整数 NN,为字符串的个数。

接下来 NN 行每行包含一个字符串,为所提供的字符串。

输出格式

输出包含一行,包含一个整数,为不同的字符串个数。

输入输出样例

输入 #1复制

5
abc
aaaa
abc
abcc
12345

输出 #1复制

4

说明/提示

对于 30\%30% 的数据:N\leq 10N≤10,M_i≈6Mi​≈6,Mmax\leq 15Mmax≤15。

对于 70\%70% 的数据:N\leq 1000N≤1000,M_i≈100Mi​≈100,Mmax\leq 150Mmax≤150。

对于 100\%100% 的数据:N\leq 10000N≤10000,M_i≈1000Mi​≈1000,Mmax\leq 1500Mmax≤1500。

样例说明:

样例中第一个字符串(abc)和第三个字符串(abc)是一样的,所以所提供字符串的集合为{aaaa,abc,abcc,12345},故共计4个不同的字符串。

Tip: 感兴趣的话,你们可以先看一看以下三题:

BZOJ3097:http://www.lydsy.com/JudgeOnline/problem.php?id=3097

BZOJ3098:http://www.lydsy.com/JudgeOnline/problem.php?id=3098

BZOJ3099:http://www.lydsy.com/JudgeOnline/problem.php?id=3099

如果你仔细研究过了(或者至少仔细看过AC人数的话),我想你一定会明白字符串哈希的正确姿势的^_^

解题思路:在稍微学习了一下set之后就非常的简单了,直接将所有的字符串放入set,然后输出set容器里面的字符串个数就可以。

AD代码:

​#include<bits/stdc++.h>
using namespace std;
set<string> a;
int main()
{
    string p;
    int n,i;
    cin>>n;
    for(i=0;i<n;i++)
    {
        cin>>p;
        a.insert(p);
    }
    cout<<a.size

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值