[NOI Online 2021 提高组] 积木小赛 - 题解
题目大意
- 给定两个字符串 \(s\) 和 \(t\),长度为 \(n\)。
- 求 \(t\) 中有几个不同的子串是 \(s\) 的子序列。
- \(n \le 3000\)。
我们首先思考如何暴力求解。考虑枚举 \(t\) 的所有子串(枚举其左端点与右端点),然后看其是否为 \(s\) 的子序列即可。总的时间复杂度为 \(O(n^3)\),无法通过。
接着考虑对暴力进行优化。在对右端点进行枚举的时候,我们可以一边枚举一边 check,总的时间复杂度降为 \(O(n^2)\),貌似可以通过。
可是真正的瓶颈才真正到来,题目中需要对字符串进行去重。第一个想法就是方便的使用 set 进行去重,核心代码如下:
cin >> n >> s >> t;
for (int i = 0; i < n; i++) {
cnt = i;
for (int k = 0; k < n; k++) {
if (cnt == n) break;
if (s[k] == t[cnt])
cnt++;
}
for (int k = i; k < cnt; k++)
ans.insert(t.substr(i, k-i+1));
}
printf("%lld", ans.size());
民间数据一测,发现有部分测试点 MLE 了,怎么办?
这说明,我们需要另辟蹊径,用另外一种方式对字符串去重。可以考虑首先计算哈希值,再使用 set 去重。结果一测,又有测试点 TLE 了,怎么办?
看来,我们不能依靠于 set 来去重。可以使用 sort 进行排序,再使用 unique 去重就可以了,完整代码如下:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int BASE = 37951, p = 1937038790342527;
int ans[10000000];
int n, cnt, v;
string s, t;
signed main() {
std::ios::sync_with_stdio(false);
cin >> n >> s >> t;
for (int i = 0; i < n; i++) {
cnt = i, v = 0;
for (int k = 0; k < n; k++) {
if (cnt == n) break;
if (s[k] == t[cnt])
cnt++;
}
for (int k = i; k < cnt; k++) {
v = (v*BASE + t[k] - 'a' + 1) % p;
ans[++ans[0]] = v;
}
}
sort(ans+1, ans+ans[0]+1);
printf("%lld", unique(ans+1, ans+ans[0]+1) - ans - 1);
return 0;
}
这样就可以勉强(真的很勉强)地卡过了。AC记录。
\(\Large \textit{Thanks for Reading!}\)

浙公网安备 33010602011771号