*题解:P14363 [CSP-S 2025] 谐音替换 / replace
解析
Solution 1
由题意得,能替换的位置一定是 \(s_1\) 能匹配上 \(t_1\) 且 \(s_2\) 能匹配上 \(t_2\) 的位置。替换是否有效就取决于 \(t_1\) 替换掉的子串左右两边的串是否和 \(t_2\) 相同。所以可以处理出 \(t_1\) 与 \(t_2\) 的最长公共前缀 (LCP) 与最长公共后缀 (LCS),利用 KMP 进行匹配,根据匹配到的位置判断是否计入答案。具体地,如果匹配到的串能够覆盖 \(t_1\) 与 \(t_2\) 不同的区间,也就是除去 LCP 和 LCS 后剩下的中间部分,则计入答案,否则说明替换后两串仍然不同,不计入答案。
需要预处理出 \(s\) 的 \(nxt\) 数组用于匹配。
时间复杂度 \(O(nL)\),期望得分 25~30。
Solution 2
令 \(s_1,s_2\) 真正起作用的那一段子串,也即 \(s_1,s_2\) 分别除去 LCP 和 LCS 的那一段子串然后拼接而成的串为 \(s'\),称 \(t_1,t_2\) 经过相同处理得到的子串为 \(t'\)。例如,对于 \(s_1=\texttt{xabcx},s_2=\texttt{xadex}\),其 LCP 为 \(\texttt{xa}\),LCS 为 \(\texttt{x}\),中间部分分别为 \(\texttt{bc}\) 和 \(\texttt{de}\),拼起来就是 \(s'=\texttt{bcde}\)。
对于一对 \(t_1,t_2\),我们只需要考虑能够使得 \(s'=t'\) 的 \(s_1,s_2\),这其实代表着至多只会有一个位置能够匹配。于是,只需要考虑 \(\operatorname{LCP}(s_1,s_2)\) 是否为 \(\operatorname{LCP}(t_1,t_2)\) 的后缀,\(\operatorname{LCS}(s_1,s_2)\) 是否为 \(\operatorname{LCS}(t_1,t_2)\) 的前缀。
这个问题可以用哈希解决,具体地,可以将 \(s_1,s_2\) 分成 LCP,\(s'\),LCS 三段进行哈希,这样就可以使用 map 存储按照 \(s'\) 的分类的 LCP 与 LCS。询问时,也对 \(t_1,t_2\) 进行三段式哈希,注意需要对 LCP 的每个后缀,LCS 的每个前缀进行哈希并分别存入一个 map 里。
时间复杂度 \(O(nq \log L)\),期望得分 50。
Solution 3
我们发现,左右两部分的匹配其实是一个匹配前缀的过程(左边那部分倒过来就从后缀变成前缀了),所以考虑能否使用字典树来解决。
首先我们要开两棵树,分别存储所有 \(s_1,s_2\) 的 LCP 的反转和 LCS,记它们为 \(LT\) 和 \(RT\)。
记 \(\operatorname{LCP}(t_1,t_2)\) 的反转在 \(LT\) 上最终到达的结点为 \(lx\),\(\operatorname{LCS}(t_1,t_2)\) 在 \(RT\) 上最终到达的结点为 \(rx\),\(\operatorname{LCP}(s_1,s_2)\) 的反转在 \(LT\) 上最终到达的结点为 \(ly\),\(\operatorname{LCS}(s_1,s_2)\) 在 \(RT\) 上最终到达的结点为 \(ry\)。那么,\(s_1,s_2\) 可以对 \(t_1,t_2\) 作贡献当且仅当 \(ly\) 是 \(lx\) 的祖先且 \(ry\) 是 \(rx\) 的祖先。也就是说,我们希望统计有多少个 \(lx\) 的祖先 \(ly\) 对应的 \(ry\) 是 \(rx\) 的祖先。于是,我们可以将询问离线下来在树上进行标记,在 \(lx\) 处标记 \(rx\) 的值,对 \(LT\) 进行 dfs,对于 dfs 过程中遇到的每个 \(ly\),对 \(RT\) 上与其对应的 \(rx\) 的子树中的点全体加 \(1\),回溯时消除影响。对于遇到的每个 \(lx\),查询 \(RT\) 上 \(rx\) 的值。查询和修改的过程可以用树状数组实现,具体地,先将 \(RT\) 按照 dfs 序编号,记为 \(dfn\),并处理出每个点的子树大小 \(siz\),然后按照 dfs 序建立树状数组,这样,子树修改就变为在 \([dfn_x,dfn_x+siz_x)\) 的范围内进行修改。
时间复杂度 \(O((n+q)\log L)\),期望得分 100。
代码
小心 \(s_1=s_2\) 与 \(|t_1|\not=|t_2|\) 的情况。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 5.2e6 + 5,M = 2e5 + 5;
struct Trie{
int pos[N][26],cnt = 1;
}tl,tr,trm;
int vm[N],mcnt,rootl[M],rootr[M];
vector<int> pos[N];
vector<pii> query[N];
int res[M];
string s1[M],s2[M],t1[M],t2[M];
int dfnl[N],dfnr[N],rcnt,siz[N];
int b[N];
void dfsr(int x){
dfnr[x] = ++rcnt;
siz[x] = 1;
for(int i=0;i<26;i++){
if(tr.pos[x][i]) dfsr(tr.pos[x][i]),siz[x] += siz[tr.pos[x][i]];
}
}
void add(int x,int k){
for(;x < N;x += x & -x){
b[x] += k;
}
}
int ask(int x){
int res = 0;
for(;x;x -= x & -x){
res += b[x];
}
return res;
}
void dfsl(int x){
for(int i : pos[x]){
add(dfnr[i],1);
add(dfnr[i] + siz[i],-1);
}
for(int i=0;i<query[x].size();i++){
pii p = query[x][i];
res[p.second] = ask(dfnr[p.first]);
}
for(int i=0;i<26;i++){
if(tl.pos[x][i]){
dfsl(tl.pos[x][i]);
}
}
for(int i : pos[x]){
add(dfnr[i],-1);
add(dfnr[i] + siz[i],1);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>s1[i]>>s2[i];
if(s1[i] == s2[i]) continue;
int m = (int)s1[i].size() - 1;
int l = 0,r = m;
while(s1[i][l] == s2[i][l]) l++;
while(s1[i][r] == s2[i][r]) r--;
string s = s1[i].substr(l,r - l + 1) + s2[i].substr(l,r - l + 1);
int mpos,rpos;
int p = 1;
for(int j=0;j<s.size();j++){
if(!trm.pos[p][s[j] - 'a']) trm.pos[p][s[j] - 'a'] = ++trm.cnt;
p = trm.pos[p][s[j] - 'a'];
}
if(!vm[p]){
vm[p] = ++mcnt;
rootl[vm[p]] = ++tl.cnt;
rootr[vm[p]] = ++tr.cnt;
}
mpos = vm[p];
p = rootr[mpos];
for(int j=r + 1;j<=m;j++){
if(!tr.pos[p][s1[i][j] - 'a']) tr.pos[p][s1[i][j] - 'a'] = ++tr.cnt;
p = tr.pos[p][s1[i][j] - 'a'];
}
rpos = p;
p = rootl[mpos];
for(int j=l - 1;j>=0;j--){
if(!tl.pos[p][s1[i][j] - 'a']) tl.pos[p][s1[i][j] - 'a'] = ++tl.cnt;
p = tl.pos[p][s1[i][j] - 'a'];
}
pos[p].push_back(rpos);
}
for(int i=1;i<=mcnt;i++){
dfsr(rootr[i]);
}
for(int i=1;i<=q;i++){
cin>>t1[i]>>t2[i];
if(t1[i].size() != t2[i].size()) continue;
int m = t1[i].size() - 1;
int l = 0,r = m;
while(t1[i][l] == t2[i][l]) l++;
while(t1[i][r] == t2[i][r]) r--;
string t = t1[i].substr(l,r - l + 1) + t2[i].substr(l,r - l + 1);
int p = 1;
bool flag = true;
int mpos,rpos;
for(int j=0;j<t.size();j++){
if(!trm.pos[p][t[j] - 'a']){
flag = false;
break;
}
p = trm.pos[p][t[j] - 'a'];
}
if(!flag) continue;
mpos = vm[p];
p = rootr[mpos];
for(int j=r + 1;j<=m;j++){
if(!tr.pos[p][t1[i][j] - 'a']){
break;
}
p = tr.pos[p][t1[i][j] - 'a'];
}
rpos = p;
p = rootl[mpos];
for(int j=l - 1;j>=0;j--){
if(!tl.pos[p][t1[i][j] - 'a']){
break;
}
p = tl.pos[p][t1[i][j] - 'a'];
}
query[p].push_back({rpos,i});
}
for(int i=1;i<=mcnt;i++){
dfsl(rootl[i]);
}
for(int i=1;i<=q;i++){
cout<<res[i]<<"\n";
}
return 0;
}

浙公网安备 33010602011771号