题目大意: 有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;
}