思路:
显然对于位置\(i\)我们要考虑的是\(i+1...n\)的限制,所以我们倒序考虑,对于第\(n\)个串,它的答案不受其他串影响,所以就是前\(n\)个串首字符的种类数即可,这样最优的包含所有字符串的至少长度为\(1\)的前缀。
假设当前位置为\(i\),我们要考虑的是当前的这个串\(s_i\)的每个前缀会对其他的字符串的答案产生怎样的影响。
我们用一个标记数组记录一下对于任意一个数组使得前缀不与\(s_{i}...s_n\)任意一个字符串的任意一个前缀相等的位置。
遍历\(s_i\)的每个前缀,假如其他串的当前位置与这个前缀相等,那么那个串就必定不能取这个前缀否则就会使得题目说的未开启的比赛发生泄漏,所以就是每次用\(s_i\)的每个前缀去更新\(map\),同时把这些相同的值删除掉表示不能使用。
每次操作完,当前\(map\)的大小即为答案,因为是从最后一个字符串的最优值更新过来的,所以当前也一定时满足题意的最好答案。
快速查询相同前缀用\(map+hash\)完成。
这道题,\(map\)会超时,建议使用\(unordered\_map/hash\_map\)。
unordered_map详解
#include <bits/stdc++.h>
#include <ext/hash_map>
using namespace std;
using namespace __gnu_cxx;
#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 1e5 + 10;
struct Hash {//一个用结构体封装的hash双模数哈希
int x,y,MOD1 = 1000000007,MOD2 = 1000000009;
Hash(){}
Hash(int _x,int _y) { x = _x; y = _y; }
Hash operator + (const Hash &a) const {
return Hash((x + a.x) % MOD1,(y + a.y) % MOD2);
}
Hash operator - (const Hash &a) const {
return Hash((x - a.x + MOD1) % MOD1,(y - a.y + MOD2) % MOD2);
}
Hash operator * (const Hash &a) const {
return Hash(1ll * x * a.x % MOD1,1ll * y * a.y % MOD2);
}
ll val() {
return 1ll * x * MOD2 + y;
}
}base(131,13331),hs[N];
int n,cur_pos[N],ans[N];//记录每个串当前去当前位置的前缀能使得字符串都是合法的前缀串
char s[N][110],len[N];
std::unordered_map<ll,vector<int> >mp;//存放每个哈希值在哪些位置出现过
//普通map超时
void solve() {
scanf("%d",&n);
for(int i = 1;i <= n;i ++) {
scanf("%s",s[i]+1);
len[i] = strlen(s[i]+1);
hs[i] = Hash(s[i][1]-'a'+1,s[i][1]-'a'+1);
mp[hs[i].val()].pb(i);
cur_pos[i] = 1;
}
for(int i = n;i >= 1;i --) {
ans[i] = sz(mp);
Hash tmp(0,0);
for(int j = 1;j <= len[i];j ++) {
tmp = tmp * base + Hash(s[i][j]-'a'+1,s[i][j]-'a'+1);
auto it = mp.find(tmp.val());
if(it == mp.end()) continue;
for(auto k : it->second) {
// cout << k << "***\n";
if(k == i) continue;
cur_pos[k]++;
hs[k] = hs[k] * base + Hash(s[k][cur_pos[k]]-'a'+1,s[k][cur_pos[k]]-'a'+1);//更新对于每个数可用合法的前缀
mp[hs[k].val()].pb(k);
}
mp.erase(tmp.val());//当前的这个前缀已经不能用了
// }
}
}
// for(int i = 1;i <= n;i ++) printf("%d : %d\n",i,cur_pos[i]);
for(int i = 1;i <= n;i ++) {
printf("%d\n",ans[i]);
}
}
int main() {
solve();
return 0;
}