做题记录 // 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 讲的 因为是班长说的 因为每次我都觉得这是一道字符串串 / 数据结构题,没想到他居然是思维!

总而言之,一个简单的点是,我们其实不用存储每个位置具体的值,只用根据操作去推某个位置的值在原字符串的哪个位置就可以了(因为操作都是复制粘贴嘛,所以不会出现新的字符)。

我们枚举 1k 中的每个位置,倒序遍历操作。假设当前位置为 i,以 p 记录最终点位。一开始 p = i,如果在 p 之前有字符串插入,我们就分讨一下:

  • 假如插入字符串后新字符串覆盖了位置 p,那么位置 p 的值就应该在被插入的字符串中;将 p 更新为插入字符串中的对应位置。
  • 否则,p 往前挪几位就可以了。

一定要注意题目中对「位置」的定义呀!我用了闭区间的写法然后上天了!

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 说要前缀和,但是我不会!摆!

好吧,其实很简单,因为这个类似于分形的构造,在记录当前前缀和后,只需要 log4n 就可以求得一个长度为 n 的字符串所需改变的字符数量……

然后我们一个深搜就可以解决了!

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

不难想到用相对关系来记录!也是一个类似于前缀和的思想!

我们记录一下前缀和,假设当前遍历到 i'O' 的数量减去 'J' 的数量是 x'I' 的数量减去 'J' 的数量是 y

那么我们用一个 map 来存储这个信息。在 x 这个 map 里记录 y 这个数量和 i 这个位置。

我们在 x 这个 map 里找到 y,然后下标一相减,三个字母不就平上了?

这里有个小细节,如果在插入之前已经有 y 这个元素不需要更新!因为我们需要串串最长,转化为下标最小。

以及如果你是在 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

为什么是字符串串而不是字符串呢?因为是班长说的!

为什么是班长说的而不是别人说的呢?因为是班长说的!

posted @   XSC062  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示