前言
很牛逼的题目,这题是要从定义出发,而非 DP,但是想到这一点不简单(我太菜了)。
思路
考虑两个名字 \(s\) 与 \(t\)。
- 如果 \(s\) 是 \(t\) 的前缀,根据字典序的规则,\(t\) 必然比 \(s\) 靠前。即 \(0\)。
- 如果 \(t\) 是 \(s\) 的前缀,同理,\(s\) 比 \(t\) 靠前。即 \(1\)。
- 否则,对于任意一个位置,\(s\) 与 \(t\) 都是等概率更靠前的。即 \(\dfrac 12\)。
所以只要找到一个串有多少个前后缀即可。假设第 \(i\) 个串有 \(a_i\) 个前缀串,\(b_i\) 个后缀串,答案即为
\[\sum\limits_{i=1}^n b_i + (n-a_i-b_i-1) \times \dfrac 12 \]显然是 trie 板子,直接做即可。注意 \(\dfrac 12\) 用逆元处理。
代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 5e5 + 5, mod = 998244353, inv = 499122177; //inv 是 2 的逆元,也就是 x/2
namespace Trie {
int tr[N][26], cnt[N]; bool ed[N];
int j, idx;
void insert(string s)
{
j = 0;
for (char si : s)
{
int i = si - 'a';
if (!tr[j][i]) tr[j][i] = ++idx;
j = tr[j][i], cnt[j]++;
}
ed[j] = true;
}
int query(string s)
{
j = 0; int ans = 0;
for (char si : s) ans += ed[j], j = tr[j][si - 'a'];
return ans;
}
}; using namespace Trie;
string a[N];
int main()
{
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i], insert(a[i]);
for (int i = 1; i <= n; i++)
{
int pre = query(a[i]), suf = cnt[j];
cout << (1 + pre + (1ll * (n - pre - suf) * inv % mod)) % mod << '\n';
}
return 0;
}
希望能帮助到大家!
标签:12,string,ABC268G,题解,tr,int,si,ed From: https://www.cnblogs.com/liangbowen/p/17435701.html