abc Replace

Source

洛谷AT_abc399_e [ABC399E] Replace

洛谷题目传送门

题目描述

给定一个正整数 N N N 以及两个长度为 N N N 的小写英文字母字符串 S S S T T T
请判断是否可以通过重复以下操作(允许 0 次操作)将 S S S 变为 T T T。若可能,还需输出所需的最小操作次数。

操作
选择两个小写英文字母 x x x y y y,将 S S S所有 出现的 x x x 替换为 y y y

输入格式

输入通过标准输入给出,格式如下:

N N N
S S S
T T T

输出格式

若可以将 S S S 变为 T T T,则输出所需的最小操作次数;否则输出 − 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 × 10 5 1 \leq N \leq 2 \times 10^5 1N2×105
  • N N N 为整数
  • S S S T T T 均为长度为 N N N 的小写英文字母字符串

样例解释 1

通过以下 4 次操作可将 S S S 变为 T T T

  1. 选择 x = x= x= b, y = y= y= c,操作后 S = S= S= afcfda
  2. 选择 x = x= x= a, y = y= y= b,操作后 S = S= S= bfcfdb
  3. 选择 x = x= x= f, y = y= y= k,操作后 S = S= S= bkckdb
  4. 选择 x = x= x= d, y = y= y= b,操作后 S = S= S= bkckbb(与 T T T 一致)

由于无法在 3 次或更少操作内完成,最小操作次数为 4。

样例解释 2

S S S T T T 初始时已一致,无需任何操作。

样例解释 3

无论如何操作,都无法将 S S S 变为 T T T

思路引入

我们将样例1转换为字符的改变,即可得到如下的图:
在这里插入图片描述
我们只需按箭头反方向改变即可,但这对这道题没有影响。
再看看样例4:
在这里插入图片描述
我们发现他形成了一个环,那是否就无解了呢???不,我们可以使用一个其他的字母来打破环。但是注意要多改一次。

思路详解

通过上面的画图我们发现一下的结论:

  1. 每个字母至多转化为另外1个字母,否则无解。
  2. 每个字母只要会转换就会产生1的贡献。
  3. 每个环将会额外产生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;
}