题目列表
AcWing 4399. 数字母
题目描述
给定一个仅包含小写字母的集合。
请你判断,集合中不同字母的数量。
输入格式 输入一行字符串,用以描述这个小写字母集合。
字符串以 { 开始,以 } 结束,中间列出所有集合中包含的小写字母,小写字母两两之间用逗号(,)加空格()隔开。
输出格式 一个整数,表示集合中不同字母的数量。
数据范围 前 5 个测试点满足,集合中包含的字母数量在 [0,10] 范围内。 所有测试点满足,集合中包含的字母数量在 [0,333] 范围内。
输入样例1: {a, b, c} 输出样例1: 3 输入样例2: {b, a, b, a} 输出样例2: 2 输入样例3: {} 输出样例3: 0
分析
统计集合中出现小写字母的种类数,如果字符串长度不大于2.说明输入是{},输出0即可,否则,输入的第1,4,7,…号元素是字母,我们遍历下所有字母,用个bool数组存储每个出现过的字母,最后统计一下即可。当然也可以直接读入每个字符,判断是不是小写字母,然后存入set中统计个数。注意:如果一次性读取行的话由于包含空格需要使用getline才能读取所有的数据。
代码
#include <iostream>
#include <string>
using namespace std;
bool cnt[26];
int main(){
string s;
getline(cin,s);
int n = s.size();
if(n <= 2) {
cout<<"0"<<endl;
}
else{
for(int i = 1;i < n;i += 3) {
cnt[s[i] - 'a'] = true;
}
int res = 0;
for(int i = 0;i < 26;i++) {
if(cnt[i]) res++;
}
cout<<res<<endl;
}
return 0;
}
AcWing 4400. 玩游戏
题目描述
n 个小朋友围成一圈,玩数数游戏。
小朋友们按顺时针顺序,依次编号为 1~n。
初始时,1 号小朋友被指定为领头人。
游戏一共会行进 k 轮。
在第 i 轮中,领头人会从他的顺时针方向的下一个人开始,按顺时针顺序数 ai 个人。
其中,最后一个被领头人数到的人被淘汰出局,这也意味着该轮游戏结束。
出局者的顺时针方向的下一个人被指定为新领头人,引领新一轮游戏。
例如,假设当游戏即将开始第 i 轮时,还剩下 5 个小朋友,编号按顺时针顺序依次为 8,10,13,14,16,并且当前领头人为 13 号小朋友,ai=12,则第 i 轮游戏结束后,最后一个被数到的小朋友为 16 号小朋友,他将被淘汰出局,并且处于其下一位的第 8 号小朋友将被指定为新领头人。
现在,请你求出每一轮次被淘汰的小朋友的编号。
输入格式 第一行包含两个整数 n,k。
第二行包含 k 个整数 a1,a2,…,ak。
输出格式 一行,k 个整数,其中第 i 个整数表示在第 i 轮中被淘汰的小朋友的编号。
数据范围 前三个测试点满足 2≤n≤10。 所有测试点满足 2≤n≤100,1≤k≤n?1,1≤ai≤109。
输入样例1: 7 5 10 4 11 4 1 输出样例1: 4 2 5 6 1 输入样例2: 3 2 2 5 输出样例2: 3 2
分析
约瑟夫环问题,只不过每次数的数字不是固定的,而是一个数组。使用数组构建单链表进行模拟,如果某轮要删除第x个小朋友,通过链表的遍历找到第x - 1个小朋友,将其后继修改为原后继的后继即可。注意本题的x可能很大,但是人数n很少,当剩下n人时,删除第x个小朋友等价于删除第x % n个小朋友,所以读入x后做下取模操作即可减少遍历的次数。
代码
#include <iostream>
using namespace std;
const int N = 105;
int ne[N];
int main(){
int n,k,t;
cin>>n>>k;
for(int i = 1;i < n;i++) ne[i] = i + 1;
ne[n] = 1;
int head = 1;//领头人
while(k--) {
cin>>t;
t = t % n + n;//不加n的话一旦t取0后面的while会陷入死循环
while(--t) head = ne[head];
cout<<ne[head]<<" ";
ne[head] = ne[ne[head]];
head = ne[head];
n--;
}
return 0;
}
AcWing 4401. 找回数组
题目描述
有一个长度为 k 的整数数组 x0,x1,…,xk?1。
不幸的是,这个数组已经丢失了,我们甚至不知道 k 的具体值。
幸运的是,我们找到了另一个利用数组 x 生成的长度为 n+1 的数组 a0,a1,…,an。
数组 a 的正式描述如下:
a0=0。 对于 1≤i≤n,ai=x(i?1)modk+ai?1。 例如,当 x=[1,2,3] 并且 n=5 时,生成数组 a 的过程如下:
a0=0 a1=x0mod3+a0=x0+0=1 a2=x1mod3+a1=x1+1=3 a3=x2mod3+a2=x2+3=6 a4=x3mod3+a3=x0+6=7 a5=x4mod3+a4=x1+7=9 所以,当 x=[1,2,3] 并且 n=5 时,可以生成数组 a=[0,1,3,6,7,9]。
现在,我们希望你通过数组 a 找回数组 x。
更具体地说,已知 1≤k≤n,请你找到所有可能的 k 值,即数组 x 的所有可能长度。
输入格式 第一行包含整数 n。
第二行包含 n 个整数 a1,a2,…,an。
注意,由于 a0 一定等于 0,所以在输入中并未给出。
输出格式 第一行输出一个整数 l,表示数组 x 的所有可能长度的数量。
第二行按升序输出 l 个整数,表示数组 x 的所有可能长度。
数据范围 前四个测试点满足 1≤n≤5, 所以测试点满足 1≤n≤1000,1≤ai≤106。
输入样例1: 5 1 2 3 4 5 输出样例1: 5 1 2 3 4 5 输入样例2: 5 1 3 5 6 8 输出样例2: 2 3 5 输入样例3: 3 1 5 3 输出样例3: 1 3
分析
题目意思是a数组由x数组生成,x数组有k个元素,a数组有n个元素,k不大于n,当i < k时,ai = ai-1 + xi-1,i >= k时,由于x只有k个元素,就用xi % k代替xi,注意这里的取模运算是作用在下标里的。现在给定a数组,求x数组所有可能的长度。 首先,a数组的长度n一定是x数组的候选长度,因为k=n时不用取模,xi-1 = ai - ai-1,这样可以求出n个x数组元素。如果k < n呢?那么只有xi%k = xi时才会合法。比如a数组为1 3 4 5,得到长度为4的x数组为1 2 1 2,如果x的长度只有2,也是可以生成相同的a数组的,因为在长度为4的x数组里xi%2 = xi。 我们推出长度为n的x数组后,就要分析缩减x的长度使得依旧能生成相同的a数组。1 2 1 2可以缩减为1 2,再比如1 2 3 1 2可以缩减为1 2 3,于是本题就转化为了求一个数组中循环节有多少种可能,这里的循环节就是数学意义上的循环节,但是由于最后一节循环数字不需要全部显示出来,循环节的种类数就增加了。 如何求循环节,假设循环节长度是k,只需要x1 = xk+1,x2 = xk+2,…即可。知道了最短循环节的长度,是否就知道了所有循环节的长度了呢,比如n = 5,a数组为1 2 1 2 1,最短循环节是1 2,长度是2,那么2的倍数4一定也是候选解,也就是1 2 1 2也是循环节。这也并不意味着我们只需要求出最短循环节长度就不需要继续枚举了。比如1 1 3 1 1,显然1 1 3是循环节,但是1 1 3 1也是循环节,所以还是需要枚举所有的k的取值才能得出最终结果的,由于n的范围是1000,枚举所有循环节的复杂度就是平方级别,不会TLE。
代码
#include <iostream>
using namespace std;
const int N = 1005;
int a[N];
bool st[N];
int main(){
int n;
cin>>n;
for(int i = 1;i <= n;i++) cin>>a[i];
for(int i = n;i >= 1;i--) a[i] -= a[i-1];//将a数组做差分转化为长度为n的x数组,注意倒着循环才不会覆盖数据
int k = 1,m = 0;
while(k <= n){
bool flag = true;
for(int i = 1;i <= k;i++) {
int p = i + k;
while(p <= n && a[i] == a[p]) p += k;
if(p <= n) {
flag = false;//不是循环节
break;
}
}
if(flag) {
st[k] = true;
m++;
}
k++;
}
cout<<m<<endl;
for(int i = 1;i <= n;i++) {
if(st[i]) cout<<i<<" ";
}
return 0;
}
|