AtCoder Beginner Contest 399-e
看到这道题,我们很容易想到这道题。是的,这道题我们也是将原问题转换成图上问题,将\(s\)字符串中每一个点和\(t\)字符串中所对应的的点之间的关系看成一条有向边,则这个问题就转换成了在一个图上找到一共有几条边,但是这里我们需要注意一个地方,就是如果图中有环,则有几个环我们就要给答案加几(因为如果有环,就不能直接交换,需要将其中一个节点赋成一个新的且没有用过的节点,这样才能保证可以成功交换,而每次赋值都要浪费一次机会,所以最后要给答案加一),这里我们还需要注意一个地方,就是如果图中有基环树(不会基环树的详见这里),就算有环,也不能给答案加一。
因为找到基环树中入度为\(2\)的节点,将它一个在环上的子节点的值改成另一个不在环上的子节点的值,其它点就按正常的方法赋值,这样的话并没有新建节点(因为父节点的两个子节点可以一起赋值,并不用新建节点或者增加步骤),也就不用给答案加一了。注意好所有细节后,我们就可以愉快的写代码了。
CODE
#include<bits/stdc++.h>
using namespace std;
int n,yb[30],fa[30],gs[30],ans=0;
string s,t;
map<char,bool> mp;
bool f[30],fl[30];
int main()
{
memset(yb,-1,sizeof(yb));
memset(fa,-1,sizeof(fa));
cin>>n>>s>>t;
if(s==t) //特判
{
cout<<0;
return 0;
}
for(int i=0;i<n;i++)
{
if(yb[s[i]-'a']!=-1 && yb[s[i]-'a']!=t[i]-'a') //如果s字符串中两个相同字符所对应的字符不同,则无论怎么交换,都不可能满足条件
{
cout<<-1;
return 0;
}
if(yb[s[i]-'a']==-1)
{
yb[s[i]-'a']=t[i]-'a'; //赋值
if(s[i]!=t[i]) fa[s[i]-'a']=t[i]-'a',gs[t[i]-'a']++; //建图
}
mp[t[i]]=true;
}
for(int i=0;i<26;i++)
{
if(fa[i]!=i && fa[i]!=-1) ans++; //边的个数
}
if(mp.size()==26) //特判,因为s中的字符种类一定大于t中的字符种类(若没有,则在上面的特判中就被特判掉了),所以若t中的字符种类满了,且s不等于t,则说明图中存在许多环,且都不是基环树,所以不可能出现新的字符来进行替换,所以不可能。
{
cout<<-1<<endl;
return 0;
}
for(int i=0;i<26;i++)
{
if(gs[i]>=2) //判断基环树(g代表着每个点的入度)
{
if(!f[i])
{
int u=i;
while(u!=-1)
{
if(f[u]) break;
f[u]=true; //标记
u=fa[u];
}
}
}
}
for(int i=0;i<26;i++)
{
if(!f[i]) //统计环的个数
{
memset(fl,false,sizeof(fl));
int u=i;
while(u!=-1)
{
if(f[u])
{
if(fl[u]) ans++; //累加答案
break;
}
f[u]=fl[u]=true;
u=fa[u];
}
}
}
cout<<ans;
return 0;
}