*题解: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;
}
posted @ 2025-11-05 17:12  yuyce  阅读(22)  评论(0)    收藏  举报