[USACO23JAN] Find and Replace S 题解
首先,我们称初始串为 $s$,目标串为 $t$,接下来把每个的 $s_i$ 向 $t_i$ 连边,不难发现,每个点出度最多为 $1$,超过 $1$ 的话就输出 $-1$。那么这张图里的每个节点只可能有多个节点连向他,他最多连向一个节点。
接下来我们对每个没遍历过的节点一遍深度优先搜索(类似建图,我们把一种字母称为一个节点),新建一个颜色,用 $ct_i=1$ 表示 $i$ 颜色的那些点是环且没有其它点连向环中节点,反之 $ct_i=0$。我们考虑 $i$ 颜色的那些点是环但有其它点连向环中节点的那种情况,假设 $u$ 连向了环中的 $v$ 节点,那么必定可以把所有 $v$ 代表的字母变成 $u$ 代表的字母,破环成链,这样就可以把连向 $v$ 的环中节点所代表的的字母都变成 $v$ 代表的字母,以此类推,最后再把 $u$ 代表的字母替换成 $v$ 代表的字母。
这是,如果存在有 $ct_i=1$,那么就证明有单独的环,需要先把任意节点替换成其它的破环成链才行,所以再用 $h_i$ 记录 $i$ 是否是非环中节点(包括自环),如果存在有 $ct_i=1$ 且所有 $h_i$ 都是 $1$,那么答案是 $-1$,否则是边数加上 $\sum ct_i$。
#include <bits/stdc++.h>
#define L(i, a, b) for(int i = a; i <= b; i++)
#define R(i, a, b) for(int i = a; i >= b; i--)
using namespace std;
const int N = 1e5 + 10;
int n, cnt, bo, ans;
int e[N], vis[52], h[52], ct[52], t[52][52];//vis[i]表示i节点的颜色
char a[N], b[N];
void Init(){
memset(t, 0, sizeof(t));
memset(h, 0, sizeof(h));
memset(ct, 0, sizeof(ct));
memset(e, -1, sizeof(e));
memset(vis, -1, sizeof(vis));
}
int Get(char c){
return c >= 'a'? c - 'a' + 26 : c - 'A';
}
int Dfs(int u, int col){
vis[u] = col;
if(e[u] != -1){
if(vis[e[u]] == col){
int v = e[u]; e[u] = -1;
ct[col] = 1, h[u] = 1;
return v;
}
if(~vis[e[u]]){
ct[vis[e[u]]] = 0, e[u] = -1;
return 52;
}
int ret = Dfs(e[u], col);
if(ret == 52) ct[vis[e[u]]] = 0;
e[u] = -1;
if(~ret){
if(ret != 52) h[u] = 1;
if(ret != u) return ret != u? ret : 52;
}
}
return -1;
}
void Solve(){
scanf("%s%s", a + 1, b + 1);
cnt = bo = ans = 0;
n = strlen(a + 1), Init();
L(i, 1, n)
t[Get(a[i])][Get(b[i])] = 1;
L(i, 0, 51){
int k = -1;
L(j, 0, 51){
if(t[i][j]){
if(~k){printf("-1\n"); return;}
k = j;
}
}
if(k == i) h[i] = 1;
else if(~k) ans++, e[i] = k;
}
L(i, 0, 51) if(vis[i] == -1) Dfs(i, i);
L(i, 0, 51){
if(!h[i]) bo = 1;
cnt += ct[i];
}
if(cnt && !bo){printf("-1\n"); return;}
ans += cnt, printf("%d\n", ans);
}
int main(){
int T; scanf("%d", &T);
while(T--) Solve();
return 0;
}