2022 ICPC 杭州 K. Master of Both gym104090K

文章描述了一种利用字典树数据结构解决具有n个字符串和q次查询的问题。每次查询涉及一个特定的字母表,要求计算字符串中的逆序对数量。通过建立字典树并记录每个节点的信息,可以有效地计算出不同字母顺序下的逆序对数目。
摘要由CSDN通过智能技术生成

Problem - K - Codeforces

题目大意: 有n个字符串和q次询问,每次询问给出一个不同的字母表,问这n个字符串中有多少逆序对

1<=n<=5e5;1<=q<=5e4;

思路:一个字符串要比另一个字符串大只要最长公共前缀后面的一个字母更大即可,所以我们建立字典树来储存n个字符串,然后记录以p为前缀的字符串数量cnt[p],然后对于每个节点,统计这个节点和子节点组成的数对数量pa,即子节点的cnt,对于每次询问,遍历每两个字母i,j,pa[i][j]即为最长公共前缀最后一个字母为i后面一个字母为j时的字符串数。一个字符串比另一个字符串大的另一个条件是字符串b是字符串a的前缀且a不等于b,此条件与字母表的顺序无关,那么我们只要在将每个字符串插入到字典树中后,统计最后一个字母的所有子节点的cnt之和,因为在树中比这个字符串更深的一定比这个字符串大

//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
int tr[N][26];
ll pa[26][26];
ll cnt[N];
int cn = 1;
ll ans1 = 0, ans2 = 0;
void insert(string s)
{//字典树
	int len = s.length(), p = 1;
	cnt[1]++;
	for (int i = 0; i < len; i++)
	{
		int ch = s[i] - 'a';
		for (int j = 0; j <= 25; j++)
		{
			if (j != ch)
			{
				pa[ch][j] += cnt[tr[p][j]];//记录当前节点有多少子节点
			}
		}
		if (!tr[p][ch])
			tr[p][ch] = ++cn;
		p = tr[p][ch];
		cnt[p]++;
	}
	for (int i = 0; i <= 25; i++)
	{
		ans1 += cnt[tr[p][i]];//记录当前字符串最后一个字母有多少子节点
	}	
}
int main()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
	int n, q;
	cin >> n >> q;
	for (int i = 1; i <= n; i++)
	{
		string s;
		cin >> s;
		insert(s);
	}
	for (int i = 1; i <= q; i++)
	{
		ans2 = 0;
		string t;
		cin >> t;
		for (int i = 0; i <= 25; i++)
		{
			for (int j = i + 1; j <= 25; j++)
			{
				ans2 += pa[t[i] - 'a'][t[j] - 'a'];
			}
		}
		cout << ans1 + ans2 << endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值