The 2022 ICPC Asia Hangzhou Regional Contest - K - 字典树
The 2022 ICPC Asia Hangzhou Regional Contest - K
以下是我的思维历程,仅供参考:
首先想暴力找两两字符串是否存在大小关系,然后进行q次,肯定会Tle
我们来试着优化找两两字符串是否存在大小关系的暴力,引用字典树,这样再插入字符串之前先查看当前字典树存在多少个字符串大于当前要插入的字符串,然后再插入,就这样进行q次
这种做法光是初始化字典树都会Tle啊啊啊,怎么办呢
我们发现不管字典序的规则怎么变,字符串数组的顺序不会改变,只有他们两两之间的关系会改变,这样的话我们统计串与串之间字母的关系,存在一个26 * 26的记录数组里
然后对于q此询问,每次都遍历以下记录数组,统计符合关系的对就行了
我还有一个小的处理,将a is a prefix of b, but a = b这个规则的数量单独记录,因为不管字典序的规则怎么变,满足这种规则的字符串对的数量不会改变
这种做法的时间复杂度是O(n + n * 26 + q * 26 * 26)不会Tle
以下是代码:
#include<bits/stdc++.h>
#define ll long long
#define N 1000010
#define endl "\n"
using namespace std;
int son[N][26], cnt[N], idx = 1;
int mp[26]; // 字典序的规则
ll tool_pair[26][26]; // 记录数组
ll base = 0; // a is a prefix of b, but a = b这种规则的数量
string str;
struct Trie
{
void insert()
{
int p = 0; //指向当前节点
for(char i : str)
{
int u = i - 'a'; //将字母转化为数字
if(!son[p][u]) son[p][u] = idx ++; //该节点不存在,创建节点
p = son[p][u]; //使 p 指向下一个节点
cnt[p] ++;
}
return ;
}
void query()
{
int p = 0; //指向当前节点
for(char i : str)
{
int u = i - 'a'; //将字母转化为数字
if(!son[p][u]) //该节点不存在,即该字符串不存在
{
for(int l = 0; l < 26; l ++)
{
int pp = son[p][l];
if(pp)
{
tool_pair[u][l] += cnt[pp];
}
}
return ;
}
for(int l = 0; l < 26; l ++)
{
if(l == u) continue ;
int pp = son[p][l];
if(pp)
{
tool_pair[u][l] += cnt[pp];
}
}
p=son[p][u]; //使 p 指向下一个节点
}
for(int i = 0; i < 26; i ++)
{
int pp = son[p][i];
if(pp)
{
base += cnt[pp];
}
}
return ;
}
}running;
void solve()
{
int n, q;
cin >> n >> q;
for(int i = 0; i < n; i ++)
{
cin >> str;
running.query();
running.insert();
}
while(q --)
{
cin >> str;
for(int i = 0; i < 26; i ++)
{
mp[str[i] - 'a'] = i;
}
ll ans = base;
for(int i = 0; i < 26; i ++)
{
for(int l = 0; l < 26; l ++)
{
if(mp[i] < mp[l])
{
ans += tool_pair[i][l];
}
}
}
cout << ans << endl;
}
return ;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while(t --)
{
solve();
}
return 0;
}
PS: ll a; int b, c; a = b + c;
计算顺序先是int + int然后再强制转化成ll,所以可能会爆掉,我的做法就是刚开始base开成int了,然后提交一直Wa啊啊啊啊啊啊,我是弱智