[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!}\)

posted @ 2021-03-30 22:54  Sparkle_ZH  阅读(73)  评论(0)    收藏  举报