千辛万苦终于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