Speed Typing
给两个字符串 I I I 和 P P P,问能否通过删除串 P P P 中的字符,使得 P P P 变成 I I I,如果能输出删除的字符数,不能输出
IMPOSSIBLE
。
模拟题。将 I I I 和 P P P 从左到右进行比对,如果不相符,则删去 P P P 中该字符,直到结束后,判断是否相同即可。 O ( n ) O(n) O(n)
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int T;
char I[100010], P[100010];
int main(){
cin >> T;
for(int t = 1; t <= T; ++t){
cout << "Case #" << t << ": ";
cin >> I >> P;
int i = strlen(I), p = strlen(P);
int ii = 0, cnt = 0;
for(int pp = 0; pp < p; ++pp){
if(ii == i) ++cnt;
else if(I[ii] == P[pp]) ++ii;
else ++cnt;
}
if(ii == i) cout << cnt << endl;
else puts("IMPOSSIBLE");
}
return 0;
}
Challenge Nine
给定一个数 N N N,在其中恰好插入一个数使得其是 9 9 9 的倍数,如果有多个答案,输出最小的。
N ≤ 1 0 123456 N\le 10^{123456} N≤10123456
如果一个数是 9 9 9 的倍数,则各个位数加起来一定是 9 9 9 的倍数,于是可以得到要插入的数 x x x。由于答案要求最小,所以采用贪心的策略,从高位到低位找到第一个小于 x x x 的数, x x x 插入到该数之前即可,注意不能有前导 0 0 0。
由于我一开始没看到要求恰好插入一个数,于是 0 0 0 的时候直接输出原串,WA了一发。
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int T;
char N[1000010];
int main(){
cin >> T;
for(int t = 1; t <= T; ++t){
cout << "Case #" << t << ": ";
cin >> N;
int sum = 0;
for(int i = 0; N[i]; ++i){
sum += N[i] - '0';
}
sum %= 9;
sum = 9 - sum;
sum %= 9;
bool flag = true;
for(int i = 0; N[i]; ++i){
if(flag == true && N[i] - '0' > sum){
if(sum == 0 && i == 0){}
else {
putchar(sum + '0');
flag = false;
}
}
putchar(N[i]);
}
if(flag == true) putchar(sum + '0');
puts("");
}
return 0;
}
Palindrome Free Strings
一开始第一眼没想出来,还以为是个丧病题。
给一个由
0
,1
,?
构成的字符串 S S S,你需要将?
替换为0
或1
,使得该字符串不包含长度大于等于 5 5 5 的回文串。
不包含长度大于等于 5 5 5 的回文串等价于不包含长度等于 5 5 5 或 6 6 6 的回文串。
所以我们可以从左到右枚举 ?
应该被替换成什么,但是只需要记录下当前位置往前
6
6
6 个字符是什么,如果往前
6
6
6 个字符可以构成长度等于
5
5
5 或
6
6
6 的回文串,则不合法。
时间复杂度 O ( 2 6 ∣ S ∣ ) O(2^6|S|) O(26∣S∣)。
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int T;
int n[20];
bool p5[1 << 5], p6[1 << 6];
void pre(){
int w6[] = {33, 18, 12};
int w5[] = {17, 10, 4};
for(int i = 0; i < (1 << 3); ++i){
int u6 = 0, u5 = 0;
for(int j = 0; j < 3; ++j){
if((i >> j) & 1){
u6 += w6[j];
u5 += w5[j];
}
}
p5[u5] = 1;
p6[u6] = 1;
}
}
bool f[50010][1 << 6];
char s[50010];
int trim(int val, int v){
val = (val << 1) | v;
val &= (1 << 6) - 1;
return val;
}
int main(){
pre();
cin >> T;
for(int t = 1; t <= T; ++t){
cout << "Case #" << t << ": ";
memset(f, 0, sizeof(f));
int n; cin >> n;
cin >> s;
f[0][0] = 1;
for(int i = 0; s[i]; ++i){
for(int j = 0; j < (1 << 6); ++j) if(f[i][j]){
if(s[i] == '1' || s[i] == '?'){
int t = trim(j, 1);
bool flag = true;
if(i >= 4 && p5[t & ((1 << 5) - 1)] == 1) flag = false;
if(i >= 5 && p6[t] == 1) flag = false;
if(flag) f[i + 1][t] = 1;
}
if(s[i] == '0' || s[i] == '?'){
int t = trim(j, 0);
bool flag = true;
if(i >= 4 && p5[t & ((1 << 5) - 1)] == 1) flag = false;
if(i >= 5 && p6[t] == 1) flag = false;
if(flag) f[i + 1][t] = 1;
}
}
}
int l = strlen(s);
bool flag = true;
for(int j = 0; j < (1 << 6); ++j) if(f[l][j] == 1){
puts("POSSIBLE");
flag = false;
break;
}
if(flag) puts("IMPOSSIBLE");
}
return 0;
}
Interesting String
当时是先做的这题,才做的前一题,因为第 3 3 3 题没看出来咋做一开始,然后还看到一堆人过人这题。瞅了一眼发现好像是数位 DP 就来试试了。
问 [ A , B ] [A,B] [A,B] 间有多少个整数满足,其各个位数之积可以被各个位数之和给整除。
1 ≤ A ≤ B ≤ 1 0 12 1\le A\le B\le10^{12} 1≤A≤B≤1012
问 [ A , B ] [A,B] [A,B] 计数可以转换为 [ 1 , B ] − [ 1 , A ] [1,B]-[1,A] [1,B]−[1,A],转成两个前缀和相减。
像这种对数的各个位数进行操作,通常可以想到数位 DP。
假设已知各个位数之和为 s s s,则就是要判断是否各个位数之积 p p p 模 s s s 是否为 0 0 0。根据 B B B 的大小可知, s s s 不会超过 110 110 110 比较小。
所以我们枚举 s s s,然后利用数位DP来求出有多少个满足和为 s s s,积在模 p p p 意义下为 0 0 0 的数。
注意前导 0 0 0 要特殊考虑。
这题好像我的做法有点儿卡常,所以需要剪剪枝。
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int T;
int n[20];
long long f[20][110][110][2][2]; //1 top, 1 leading zero
long long calc(long long v){
if(v == 0) return 0;
int p = 0;
long long tmp = v;
while(tmp > 0){
n[p] = tmp % 10;
++p;
tmp /= 10;
}
long long ans = 0;
for(int i = 1; i < 110; ++i){
memset(f, 0, sizeof(f));
f[p][0][1 % i][1][1] = 1;
for(int id = p; id > 0; id--){
for(int sum = 0; sum <= i; ++sum){
for(int prod = 0; prod < i; ++prod){
if(!f[id][sum][prod][0][0] && !f[id][sum][prod][1][0] && !f[id][sum][prod][0][1] && !f[id][sum][prod][1][1]) continue;
if(0 < n[id - 1]){
f[id - 1][sum][0][0][0] += f[id][sum][prod][1][0];
f[id - 1][sum][prod][0][1] += f[id][sum][prod][1][1];
}
if(0 == n[id - 1]){
f[id - 1][sum][0][1][0] += f[id][sum][prod][1][0];
f[id - 1][sum][prod][1][1] += f[id][sum][prod][1][1];
}
f[id - 1][sum][prod][0][1] += f[id][sum][prod][0][1];
f[id - 1][sum][0][0][0] += f[id][sum][prod][0][0];
for(int num = 1; num < 10; ++num){
if(num < n[id - 1]){
f[id - 1][sum + num][prod * num % i][0][0] += f[id][sum][prod][1][1] +
f[id][sum][prod][1][0];
}
if(num == n[id - 1]){
f[id - 1][sum + num][prod * num % i][1][0] += f[id][sum][prod][1][1] +
f[id][sum][prod][1][0];
}
f[id - 1][sum + num][prod * num % i][0][0] += f[id][sum][prod][0][1] +
f[id][sum][prod][0][0];
}
}
}
}
ans += f[0][i][0][0][0] + f[0][i][0][1][0] + f[0][i][0][0][1] + f[0][i][0][1][1];
}
return ans;
}
int main(){
cin >> T;
for(int t = 1; t <= T; ++t){
cout << "Case #" << t << ": ";
long long A, B;
cin >> A >> B;
cout << calc(B) - calc(A - 1) << endl;
}
return 0;
}