abc Replace
洛谷AT_abc399_e [ABC399E] Replace
洛谷题目传送门
题目描述
给定一个正整数 NNN 以及两个长度为 NNN 的小写英文字母字符串 SSS 和 TTT。
请判断是否可以通过重复以下操作(允许 0 次操作)将 SSS 变为 TTT。若可能,还需输出所需的最小操作次数。
操作:
选择两个小写英文字母 xxx 和 yyy,将 SSS 中 所有 出现的 xxx 替换为 yyy。
输入格式
输入通过标准输入给出,格式如下:
NNN
SSS
TTT
输出格式
若可以将 SSS 变为 TTT,则输出所需的最小操作次数;否则输出 −1-1−1。
输入输出样例 #1
输入 #1
6
afbfda
bkckbb
输出 #1
4
输入输出样例 #2
输入 #2
4
abac
abac
输出 #2
0
输入输出样例 #3
输入 #3
4
abac
abrc
输出 #3
-1
输入输出样例 #4
输入 #4
4
abac
bcba
输出 #4
4
说明/提示
约束条件
- 1≤N≤2×1051 \leq N \leq 2 \times 10^51≤N≤2×105
- NNN 为整数
- SSS 和 TTT 均为长度为 NNN 的小写英文字母字符串
样例解释 1
通过以下 4 次操作可将 SSS 变为 TTT:
- 选择 x=x=x=
b
, y=y=y=c
,操作后 S=S=S=afcfda
- 选择 x=x=x=
a
, y=y=y=b
,操作后 S=S=S=bfcfdb
- 选择 x=x=x=
f
, y=y=y=k
,操作后 S=S=S=bkckdb
- 选择 x=x=x=
d
, y=y=y=b
,操作后 S=S=S=bkckbb
(与 TTT 一致)
由于无法在 3 次或更少操作内完成,最小操作次数为 4。
样例解释 2
SSS 与 TTT 初始时已一致,无需任何操作。
样例解释 3
无论如何操作,都无法将 SSS 变为 TTT。
思路引入
我们将样例1转换为字符的改变,即可得到如下的图:
我们只需按箭头反方向改变即可,但这对这道题没有影响。
再看看样例4:
我们发现他形成了一个环,那是否就无解了呢???不,我们可以使用一个其他的字母来打破环。但是注意要多改一次。
思路详解
通过上面的画图我们发现一下的结论:
- 每个字母至多转化为另外1个字母,否则无解。
- 每个字母只要会转换就会产生1的贡献。
- 每个环将会额外产生1的贡献。
但是我们要注意,一个环需要一个空闲字母或一个链首(入度为0)的字母才能打破,所以没有这些字母又有环依旧无解。
现在代码已经很显然了,具体如下。
code
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5;
int n;
string s,t;
int nxt[35],in[35];
int vis[35];
void dfs(int u){//深搜标记if(vis[u])return;vis[u]=1;if(nxt[u]!=-1)dfs(nxt[u]);
}
int flag=0;//有无字母可以打破环
int ans=0;
int main(){cin>>n>>s>>t;memset(nxt,-1,sizeof(nxt));//nxt==-1代表当前字母不变for(int i=0;i<n;i++){int x=s[i]-'a',y=t[i]-'a';if(nxt[x]==-1)nxt[x]=y;else if(nxt[x]!=y){//一个字母至多变为另一个字母cout<<-1;return 0;}}for(int i=0;i<26;i++){if(nxt[i]!=-1){//统计入度,找到链头in[nxt[i]]++;ans+=(nxt[i]!=i);//有可能有自环,判断一下}}for(int i=0;i<26;i++){if(nxt[i]==-1||in[i]==0)flag=1;}for(int i=0;i<26;i++){//标记自环和链if(nxt[i]==i||in[i]==0)dfs(i);}for(int i=0;i<26;i++){if(!vis[i]){ans++;//每一个环额外有1的贡献dfs(i);if(!flag){cout<<-1<<'\n';return 0;}}}cout<<ans;return 0;
}