Codeforces1538E Funny Substrings (字符串合并求贡献)

题目链接: Funny Substrings

大致题意

有n个操作, 询问执行完n个操作后, 最后的字符串中包含多少个"haha".

操作有两种:
​ 操作①: x := s字符串s赋值给变量x.
​ 操作②: x = a + b变量x 修改为 变量a + 变量b, 其中两个变量相加表示两个字符串的拼接.

解题思路

字符串合并 (下文中没有前缀和一词, 想表达的意思均为 前缀 和 xxxxxx)

首先我们明确一点, 如果字符串的长度只有1, 那么把这个字符串自我合并n次后, 长度可达2n. 因此直接暴力模拟是不成立的.

考虑到题目中需要我们计算出最终的字符串中含有多少个"haha", 我们发现对于每个操作①类型的字符串, 可以直接求出有多少个"haha"串.
当遇到操作②类型的字符串时(假设进行操作a = b + c), 对于a中的"haha"串, 我们可以分成三类去看: b中原有的, c中原有的, b和c拼接后新产生的. 我们重点需要考虑拼接后新产生的"haha", 一定是由b串的一个严格后缀+c串的一个严格前缀得到的(严格前后缀: 一个非空的前后缀子串).


到此为止, 我们的大概思路就成型了, 对于每一个变量, 我们需要记录这个变量的前缀字符串, 后缀字符串, 当前字符串内的答案.

对于操作①, (假设进行操作 x := a) 我们记录下a的前缀 和 后缀, 以及 a中的haha数量,. { pre, suf, ans }.
对于操作②, (假设进行操作a = b + c) 我们取b的前缀作为a的前缀, c的后缀作为a的后缀, 以及两者原有贡献和新贡献作为a的答案. { b.pre, c.suf, b.ans + c.ans + newans }


此时有一个很重要的问题, 我们需要记录多长的前缀 和 后缀?

考虑到我们记录前缀, 后缀的目的是, 计算两个串合并后产生的新"haha"串, 用到的是严格前后缀, 因此我们记录3个字符即可.(因为是严格前后缀, 最大拆分为3, 1).


综上所述, 我们可以保证我们的统计方式不重不漏.

关于代码方面, 和上述思路的实现略有改变(为了好写), 详细看注释即可.

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
struct node { string str; ll cou; }; //str存储前后缀, 前3个为前缀, 后3个为后缀, 保证长度<=6, cou为结果.

int fact(const string& s) { //计算s串中有多少个"haha"
	int res = 0;
	for (int i = 0; i < s.length(); ++i) {
		if (s.substr(i, 4) == "haha") res++;
	}
	return res;
}

string getpre(const string& s) { return s.substr(0, 3); } //得到前缀
string getsuf(const string& s) { return s.substr(max<int>(0, s.size() - 3), 3); } //得到后缀

node merge(const node& a, const node& b) { //操作②, 合并字符串
	return { getpre(a.str) + getsuf(b.str), a.cou + b.cou + fact(getsuf(a.str) + getpre(b.str)) };
}
int main()
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);

	int t; cin >> t;
	while (t--) {
		unordered_map<string, node> mp; //哈希表, 映射变量名和结构体

		int n; cin >> n;
		rep(i, n) {
			string a, b; cin >> a >> b;
			if (b.front() == ':') { //操作①
				cin >> b;
				mp[a] = { b, fact(b) }; //题目保证串长<=5, 因此直接存储即可
			}
			else {
				string c; cin >> b >> c >> c;
				mp[a] = merge(mp[b], mp[c]);
			}

			if (i == n) cout << mp[a].cou << endl;
		}
	}
	return 0;
}

大模拟, 狗都不写! 哦, 50行不到啊, 那没事儿了!

END

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值