题目链接
https://codeforces.com/gym/104090/problem/K
思路
本题考虑使用字典树。
将输入的字符串依次插入到字典树中。我们发现,在插入字符串时,我们可以用当前正在插入的字符对应节点的兄弟节点计算贡献。
注意:当新插入的字符串没有产生新的节点,且在原字典树的基础上没有遍历到根节点时为一种特殊情况(已在代码中给出)
d
p
i
,
j
dp_{i,j}
dpi,j表示在字典树中,新增字符为
i
i
i时,该字符所在节点的兄弟节点上的字符
j
j
j的数量。
预处理出
d
p
dp
dp数组之后,对于每一个询问,我们只需要根据新的字典序和
d
p
dp
dp数组便可以直接
(
2
6
2
)
(26^2)
(262)计算答案。
时间复杂度:
O
(
∑
∣
s
i
∣
×
26
+
q
×
2
6
2
)
O(\sum|s_{i} |\times 26 + q\times 26^{2} )
O(∑∣si∣×26+q×262)
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5 + 5, M = 1e6 + 5;
int n, q;
string s, pa;
int son[M][26], idx, cnt[M];
int dp[26][26];
void insert(int id, string &str)
{
int p = 0;
for (int i = 0; i < str.size(); i++)
{
int u = str[i] - 'a';
if (!son[p][u])
{
son[p][u] = ++idx;
}
for (int j = 0; j < 26; j++)
{
if (j == u) continue;
if (son[p][j])
{
dp[u][j] += cnt[son[p][j]];
}
}
p = son[p][u];
cnt[p]++;
}
//特殊情况
int c = str[str.size() - 1] - 'a';
for (int i = 0; i < 26; i++)
{
if (son[p][i])
{
dp[c][c] += cnt[son[p][i]];
}
}
}
void solve()
{
cin >> n >> q;
for (int i = 1; i <= n; i++)
{
cin >> s;
insert(i, s);
}
while (q--)
{
cin >> pa;
int ans = 0;
for (int i = 0; i < pa.size(); i++)
{
int high = pa[i] - 'a';
for (int j = i; j < pa.size(); j++)
{
int low = pa[j] - 'a';
ans += dp[high][low];
}
}
cout << ans << endl;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve();
}
return 0;
}
测试样例
test1
2 1
abcde
abcd
abcdefghijklmnopqrstuvwxyz
答案为1
test2
7 1
ywez
gge
nporgvazf
np
fhrqoami
x
mxqxpvyt
abcdefghijklmnopqrstuvwxyz
答案为13