专栏文章
论如何用六级算法爆切 [CSP-S 2025] 谐音替换
P14363题解参与者 12已保存评论 13
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 13 条
- 当前快照
- 1 份
- 快照标识符
- @minedm0u
- 此快照首次捕获于
- 2025/12/02 01:02 3 个月前
- 此快照最后确认于
- 2025/12/02 01:02 3 个月前
下列讨论自动忽略 和 的情况。
一个替换 的本质是将一个子串替换为另一个子串,且这两个串没有公共前缀或者后缀。我们令 和 分别等于 和 ,其中 和 没有公共前缀或后缀。
我们不妨对于 建立字典树,其中 表示 翻转后的串。
接下来我们处理查询。一个查询的本质是要将一个子串替换为另一个子串且两个子串没有公共前缀或后缀。我们令 和 分别等于 和 。接下来我们考虑在字典树上查询。我们发现,符合要求的 一定满足其本质与查询串的本质相同,且 和 分别是 和 的后缀和前缀。于是我们考虑枚举 ,令当前枚举的 为 ,那么只需要查询当前枚举的 下,前缀满足条件的 的数量就可以了。
我们在每一个 对应节点的子树内,做一个树上前缀和,记录这个子树的根节点到每个节点的链上有多少个 ,那么我们要查询的就是 节点上存的值。我们发现,可以使用
std::unordered_map 存储哈希值对应的节点上的值,即可 查询。现在只剩下了一个问题,即可能并不存在这样的节点。于是我们可以二分最长的存在 节点的 的后缀 ,复杂度 。这样我们就解决了整个问题。在实现的时候,不加入字符 也可以。总复杂度 。
代码(赛后重写):
CPP#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const int P = 1e9+7, Q=1e9+9, B=457, C=997;
ll next_hash (ll prev, int chr)
{
ll a = prev >> 32, b = prev & -1u; chr ++;
a = (a * B + chr) % P;
b = (b * C + chr) % Q;
return (a << 32) | b;
}
const int maxl = 5002424;
const int maxn = 200083;
struct node
{ int pt, nxt [27]; }
trie [maxl*2+maxn];
struct pn
{
int rt;
unordered_map <ll, int> mp;
} pns [maxn];
int ctc, ptc;
char s [maxl], t [maxl];
void q_diff (int len, int& dnq, int& dxq)
{
int nq = len+1, xq=-1;
int i;
for (i=0; i<len; i++)
if (s [i] != t [i])
nq = min (nq, i), xq = max (xq, i);
dnq = nq; dxq = xq;
}
#define jnode(var,chr) if(trie[var].nxt[chr])var=trie[var].nxt[chr];else var=trie[var].nxt[chr]=++ctc
#define ord(ch) (ch=='#'?26:ch-'a')
void add_s (int idx)
{
scanf ("%s%s", s, t);
int len = strlen (s);
if (strcmp (s, t) == 0) return;
int nq, xq; q_diff (len, nq, xq);
int i, cur = 0;
for (i=nq; i<=xq; i++)
{
jnode (cur, ord (s [i]));
jnode (cur, ord (t [i]));
}
jnode (cur, 26);
for (; i<len; i++)
jnode (cur, ord (s [i]));
int pt = trie [cur] .pt;
if (pt == 0) pt = trie [cur] .pt = ++ptc;
cur = pns [pt] .rt;
if (cur == 0) cur = pns [pt] .rt = ++ctc;
for (i=nq-1; i>=0; i--)
jnode (cur, ord (s [i]));
trie [cur] .pt ++;
}
void dfs_pn (int rt, int cur, ll ch, int cn)
{
pns [rt] .mp [ch] = cn = trie [cur] .pt += cn;
int i, m;
for (i=0; i<27; i++)
if (m = trie [cur] .nxt [i])
dfs_pn (rt, m, next_hash (ch, i), cn);
}
ll hashes [maxl];
int query (void)
{
scanf ("%s%s", s, t);
int len = strlen (s);
if (strlen (t) != len) return 0;
int nq, xq; q_diff (len, nq, xq);
int i, cur = 0;
for (i=1; i<=nq; i++) hashes [i] = next_hash (hashes [i-1], ord (s [nq-i]));
for (i=nq; i<=xq; i++)
{
if (0 == (cur = trie [cur] .nxt [ord (s [i])])) return 0;
if (0 == (cur = trie [cur] .nxt [ord (t [i])])) return 0;
}
if (0 == (cur = trie [cur] .nxt [26])) return 0;
int ans = 0;
for (i=xq; i<len; i++)
{
if (trie [cur] .pt != 0)
{
int l = 0, r = nq;
pn& cpn = pns [trie [cur] .pt];
while (l < r)
{
int m = l+r+1 >> 1;
if (cpn.mp.count (hashes [m])) l = m;
else r = m-1;
}
ans += cpn.mp [hashes [l]];
}
if (i != len-1)
if (0 == (cur = trie [cur] .nxt [ord (t [i+1])]))
break;
}
return ans;
}
int main (void)
{
int n, q; scanf ("%d%d", &n, &q);
int i; for (i=1; i<=n; i++) add_s (i);
for (i=1; i<=ptc; i++) dfs_pn (i, pns [i] .rt, 0, 0);
for (i=1; i<=q; i++) printf ("%d\n", query ());
}
相关推荐
评论
共 13 条评论,欢迎与作者交流。
正在加载评论...