[CSP-S 2025] 谐音替换题解

暴力

暴力枚举使用哪个二元组去替换这个 \(s\),我们采用系统函数 \(find\) 和 \(substr\),每次在 \(t1\) 中寻找一个 \(s1\) 替换成 \(s2\) ,判断是否会变成 \(t2\)。同时会有几个不需要处理的情况,需要特判,\(1.\) \(|t1| != |t2|\) 怎么变都不会相等,\(2.\) \(s1=s2\) 则变了等于没变。

时间复杂度是 \(O(nq|t|)\),期望得到 \(30pts\),实际得到 \(35pts\)。

代码
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int n,q;
string s1[N],s2[N];
string t1,t2;
string s;
void solve(){
	while(q--){
		cin>>t1>>t2;
		int ans=0;
		if(t1.size()!=t2.size()){
			cout<<0<<"\n";
			continue; 
		}
		for(int i=1;i<=n;i++){
			if(s1[i]==s2[i])continue;
			int pos=t1.find(s1[i]);
			while(pos!=-1){
				s=t1.substr(0,pos)+s2[i]+t1.substr(pos+s2[i].size());
				ans+=(s==t2);
				pos=t1.find(s1[i],pos+1);
			}
		}
		cout<<ans<<"\n";
	}
	return;
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>q;
	for(int i=1;i<=n;i++)cin>>s1[i]>>s2[i];
	solve();
	return 0;
} 

寻找性质

发现对于一组可行的 \(s1,s2\),我们设其中不同的部分为 \(w1\),\(t1,t2\) 的不同部分设为 \(w2\),那样例来说,\(xabcx\ xadex\) 的不同就是 \(bc\) 和 \(de\),所以它们的 \(w\) 就是 \(bcde\)。而除去了 \(w1\) 以外的部分都是相同的,不然就不可行了。那么其一定会满足条件 \(w1=w2\),不然也不可行。因此我们可以将每一对 \(s1,s2\) 分成三个部分,分别是两个相同的部分,和中间不同的部分。对于上面的例子,三个部分就分别是 \(xa\ bcde\ x\),那么原来就不必有原来繁琐的寻找了,我们分别记为 \(p,m,b\),对于 \(t\) 的记为 \(P,M,B\)。

那么判断合法就可以变为 \(m=M\) 且 \(p\) 为 \(P\) 的后缀 \(b\) 为 \(B\) 的前缀,就满足了 \(t1\) 与 \(t2\) 不同的地方被改变,相同的地方没有改变。通过枚举 \(n\) 个 \(s\) 来判断即可,时间复杂度变为 \(O(nq)\)。期望得到 \(40pts\) 实际可以得到 \(50pts\)。

代码
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int n,q;
string p[N],m[N],b[N];
string t1,t2;
string P,M,B;
string s1[N],s2[N];
void solve(){
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		cin>>s1[i]>>s2[i];
		if(s1[i]==s2[i])continue;
		int l=0,r=s1[i].size()-1;
		while(s1[i][l]==s2[i][l])l++;
		while(s1[i][r]==s2[i][r])r--;
		p[i]=s1[i].substr(0,l);
		m[i]=s1[i].substr(l,r-l+1)+s2[i].substr(l,r-l+1);
		b[i]=s1[i].substr(r+1);
	}
	while(q--){
		cin>>t1>>t2;
		if(t1.size()!=t2.size()){
			cout<<0<<"\n";
			continue;
		}
		int l=0,r=t1.size()-1;
		while(t1[l]==t2[l])l++;
		while(t1[r]==t2[r])r--;
		P=t1.substr(0,l);
		M=t1.substr(l,r-l+1)+t2.substr(l,r-l+1);
		B=t1.substr(r+1);
		int ans=0;
		for(int i=1;i<=n;i++){
			if(s1[i]==s2[i])continue;
			if(m[i]!=M)continue;
			if(p[i].size()>P.size() || P.substr(P.size()-p[i].size())!=p[i])continue;
			if(b[i].size()>B.size() || B.substr(0,b[i].size())!=b[i])continue;
			ans++;
		}
		cout<<ans<<"\n";
	}
	return;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	solve();
	return 0;
} 
/*
1 1
bc de
xabcx xadex
*/

优化

发现现在的瓶颈存在于每次都需要去枚举替换的字符串,所以我们应该优化这个寻找的过程,也就是多字符串匹配。那么就有一个专门解决这个问题的算法 \(AC\) 自动机,由于需要区分开三个部分,所以在将三个部分合并的时候需要依次在中间插入 '#' 号,用来防止三个部分混淆。期望得到 \(100pts\) 实际得到 \(100pts\)。

代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=200005;
int n,q;
string s;
string t1,t2;
string P,M,B;
string s1[N],s2[N];
int to[5000005][27];
int tot;
int sum[5000005];
int fail[5000005];
void insert(string f){
    int u=0;
    for(auto i:f){
        int c=(i=='#' ? 26 : i-'a');
        if(!to[u][c])to[u][c]=++tot;
        u=to[u][c];
    }
    sum[u]++;
    return;
}
void bfs(){
    queue<int>q;
    for(int i=0;i<=26;i++){
        if(to[0][i])q.push(to[0][i]);
    }
    while(!q.empty()){
        int u=q.front();q.pop();
        sum[u]+=sum[fail[u]];
        for(int i=0;i<=26;i++){
            if(to[u][i]){
                fail[to[u][i]]=to[fail[u]][i];
                q.push(to[u][i]);
            }else to[u][i]=to[fail[u]][i];
        }
    }
    return;
}
int query(string f){
    int u=0,ans=0;
    for(auto i:f){
        int c=(i=='#' ? 26 : i-'a');
        ans+=sum[u=to[u][c]];
    }
    return ans;
}
void solve(){
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		cin>>s1[i]>>s2[i];
		if(s1[i]==s2[i])continue;
		int l=0,r=s1[i].size()-1;
		while(s1[i][l]==s2[i][l])l++;
		while(s1[i][r]==s2[i][r])r--;
        s=s1[i].substr(0,l)+'#'+s1[i].substr(l,r-l+1)+'#'+s2[i].substr(l,r-l+1)+'#'+s1[i].substr(r+1);
        insert(s);
	}
    bfs();
	while(q--){
		cin>>t1>>t2;
		if(t1.size()!=t2.size()){
			cout<<0<<"\n";
			continue;
		}
		int l=0,r=t1.size()-1;
		while(t1[l]==t2[l])l++;
		while(t1[r]==t2[r])r--;
        s=t1.substr(0,l)+'#'+t1.substr(l,r-l+1)+'#'+t2.substr(l,r-l+1)+'#'+t1.substr(r+1);
		cout<<query(s)<<"\n";
	}
	return;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	solve();
	return 0;
} 
/*
1 1
bc de
xabcx xadex
*/
posted @ 2026-09-08 13:04  tangkaiming  阅读(37)  评论(2)    收藏  举报