做题记录 // 230224
GM 说字符串串题是 KMP 的基础,可是 GM 出的串串题和 KMP 有什么关系呢?以及,GM 不是讲过 KMP 吗?哦,好像是三哥讲的。
A. 乒乓球
http://222.180.160.110:1024/contest/3352/problem/1
首先是一个简单的模拟啊。我们直接用 std::string
输入,如果查找到 'E'
就停止。
不过 string::find()
的返回值是一个字符串!很吓人,返回的是待查找内容所在的可见子串。如果找不到就会返回 std::npos
,是一个值为 -1
的常量。
但是题目为什么会强调一个胜两颗球才算赢呢?样例没有体现这一点!其实意思就是说,如果碰到了 "WLWLWLWLWLWL..."
这种情况,就要一直不停地比下去,直到分差大于等于 2 为止。
很尴尬!我在 gm 提示之前并没有考虑到输入开头就是 'E'
的情况。所以我们特判一下,如果开头是 'E'
那就输出两个 0:0
。
又很尴尬!gm 说我是错的。哪里错了呢?我乱举了几个特例发现我没有考虑 ELE
之类的情况,因为我的 'E'
是从后往前判的!神奇!
最后还是错了!错误的原因如下:
- 所谓 11 分制不是说打了 11 盘就停,是两个人当中有 11 分的才停……
- 最后一盘不管开始打没有都要输出!所以特判可以删掉了!
namespace XSC062 {
using namespace fastIO;
using str = std::string;
str s, t;
int x, y, cnt;
inline int abs(int x) {
return x >= 0 ? x : -x;
}
int main() {
#ifdef ONLINE_JUDGE
freopen("ball.in", "r", stdin);
freopen("ball.out", "w", stdout);
#endif
while (std::cin >> t) {
s += t;
if (t.find('E') != str::npos)
break;
}
if (s.front() == 'E') {
printf("0:0\n\n0:0");
return 0;
}
for (int i = 0; i < (int)s.length(); ++i) {
if (s[i] == 'E') {
while ((int)s.size() > i)
s.pop_back();
break;
}
}
for (auto i : s) {
(i == 'W') ? ++x : ++y;
if (++cnt >= 11 && abs(x - y) >= 2) {
printf("%d:%d\n", x, y);
x = y = cnt = 0;
}
}
if (cnt != 0) {
printf("%d:%d\n", x, y);
x = y = cnt = 0;
}
putchar('\n');
for (auto i : s) {
(i == 'W') ? ++x : ++y;
if (++cnt >= 21 && abs(x - y) >= 2) {
printf("%d:%d\n", x, y);
x = y = cnt = 0;
}
}
if (cnt != 0) {
printf("%d:%d\n", x, y);
x = y = cnt = 0;
}
return 0;
}
} // namespace XSC062
B. 复制粘贴 2
http://222.180.160.110:1024/contest/3352/problem/2
为什么每次碰到这道题我都不会呢?因为是 lhy 讲的 因为是班长说的 因为每次我都觉得这是一道字符串串 / 数据结构题,没想到他居然是思维!
总而言之,一个简单的点是,我们其实不用存储每个位置具体的值,只用根据操作去推某个位置的值在原字符串的哪个位置就可以了(因为操作都是复制粘贴嘛,所以不会出现新的字符)。
我们枚举 中的每个位置,倒序遍历操作。假设当前位置为 ,以 记录最终点位。一开始 p = i
,如果在 之前有字符串插入,我们就分讨一下:
- 假如插入字符串后新字符串覆盖了位置 ,那么位置 的值就应该在被插入的字符串中;将 更新为插入字符串中的对应位置。
- 否则, 往前挪几位就可以了。
一定要注意题目中对「位置」的定义呀!我用了闭区间的写法然后上天了!
namespace XSC062 {
using namespace fastIO;
const int maxn = 2e5 + 5;
int k, m, n, p;
char s[maxn], t[maxn];
int l[maxn], r[maxn], w[maxn];
int main() {
read(k), read(m);
scanf("%s", s + 1);
read(n);
for (int i = 1; i <= n; ++i) {
read(l[i]), read(r[i]);
read(w[i]), ++l[i];
}
for (int i = 1; i <= k; ++i) {
p = i;
// printf("i = %d:\n p = %d\n", i, p);
for (int j = n; j; --j) {
if (w[j] < p && w[j] + r[j] - l[j] + 1 >= p)
p = l[j] + p - w[j] - 1;
else if (w[j] < p)
p -= r[j] - l[j] + 1;
// printf(" p = %d\n", p);
}
putchar(s[p]);
}
return 0;
}
} // namespace XSC062
C. 愉快的标志设计
http://222.180.160.110:1024/contest/3352/problem/3
哈哈哈,円状 JOI,只能说霓虹文化也挺博大精深!
GM 说要前缀和,但是我不会!摆!
好吧,其实很简单,因为这个类似于分形的构造,在记录当前前缀和后,只需要 就可以求得一个长度为 的字符串所需改变的字符数量……
然后我们一个深搜就可以解决了!
namespace XSC062 {
using namespace fastIO;
const int inf = 0x3f3f3f3f;
const int maxn = (1 << 20) + 5;
char s[maxn << 2];
int n, k, res = inf;
int cj[maxn << 2], co[maxn << 2], ci[maxn << 2];
inline int min(int x, int y) {
return x < y ? x : y;
}
int DFS(int l, int r, int k) {
if (l == r)
return 0;
int res = k - (cj[l + k - 1] - cj[l - 1])
+ k - (co[l + 2 * k - 1] - co[l + k - 1])
+ k - (ci[l + 3 * k - 1] - ci[l + 2 * k - 1]);
return res + DFS(l + 3 * k, r, k / 4);
}
int main() {
scanf("%d %s", &k, s + 1);
n = (1 << (k * 2));
for (int i = 1; i <= n; ++i) {
cj[i] = cj[i - 1] + (s[i] == 'J');
co[i] = co[i - 1] + (s[i] == 'O');
ci[i] = ci[i - 1] + (s[i] == 'I');
}
for (int i = 1; i <= n; ++i) {
s[i + n] = s[i];
cj[i + n] = cj[i + n - 1] + (s[i + n] == 'J');
co[i + n] = co[i + n - 1] + (s[i + n] == 'O');
ci[i + n] = ci[i + n - 1] + (s[i + n] == 'I');
res = min(res, DFS(i, i + n - 1, n / 4));
}
print(res);
return 0;
}
} // namespace XSC062
int main() {
XSC062::main();
return 0;
}
D. JOIOJI
http://222.180.160.110:1024/contest/3352/problem/4
不难想到用相对关系来记录!也是一个类似于前缀和的思想!
我们记录一下前缀和,假设当前遍历到 ,'O'
的数量减去 'J'
的数量是 ,'I'
的数量减去 'J'
的数量是 。
那么我们用一个 map
来存储这个信息。在 这个 map
里记录 这个数量和 这个位置。
我们在 这个 map
里找到 ,然后下标一相减,三个字母不就平上了?
这里有个小细节,如果在插入之前已经有 这个元素不需要更新!因为我们需要串串最长,转化为下标最小。
以及如果你是在 at 上交的需要输出文末回车才可以!
namespace XSC062 {
#define mkp std::make_pair
const int maxn = 2e5 + 5;
using pii = std::pair<int, int>;
char s[maxn];
int n, cj, co, ci, p, t, res;
std::vector<pii> g[maxn << 1];
inline int max(int x, int y) {
return x > y ? x : y;
}
int main() {
scanf("%d %s", &n, s + 1);
for (int i = 1; i <= n; ++i) {
if (s[i] == 'J')
++cj;
else if (s[i] == 'O')
++co;
else ++ci;
t = co - cj + n;
p = std::lower_bound(g[t].begin(),
g[t].end(), mkp(ci - cj, i)) - g[t].begin();
if (p >= (int)g[t].size())
g[t].push_back(mkp(ci - cj, i));
else res = max(res, i - g[t][p].second + 1);
}
printf("%d", res);
return 0;
}
} // namespace XSC062
E. 统计单词数
http://222.180.160.110:1024/contest/3352/problem/5
很简单的模拟!GM 说这个和明天要讲的暴力匹配很像!到底哪里像了?
还是挂了!因为我只判了单词后面是不是空格,但是没有判单词前面有没有!我是聪明!
namespace XSC062 {
using namespace fastIO;
using str = std::string;
str t, s;
int cnt, res = -1;
inline char f(char ch) {
if ('A' <= ch && ch <= 'Z')
return ch - 'A' + 'a';
return ch;
}
int main() {
#ifdef ONLINE_JUDGE
freopen("stat.in", "r", stdin);
freopen("stat.out", "w", stdout);
#endif
std::getline(std::cin, t, '\n');
std::getline(std::cin, s, '\n');
for (int i = 0; i < (int)s.size(); ++i) {
if (s[i] == ' ')
continue;
for (int j = 0; j < (int)t.size(); ++j) {
if (f(t[j]) != f(s[i + j]))
goto NoSol;
}
if (s[i + t.size()] != ' '
|| (i != 0 && s[i - 1] != ' '))
goto NoSol;
++cnt;
if (res == -1)
res = i;
NoSol: ;
}
if (cnt == 0)
puts("-1");
else print(cnt, ' '), print(res);
return 0;
}
} // namespace XSC062
为什么是字符串串而不是字符串呢?因为是班长说的!
为什么是班长说的而不是别人说的呢?因为是班长说的!
—— · EOF · ——
真的什么也不剩啦 😖
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】