【CFR#659(Div2) E】String Transformation 2
题目
给出长度为\(n\)的字符串\(A\) 和\(B\),求将\(A\)变为\(B\) 最少操作次数
一次操作为:
选择一些位置$p_1 , p_2 ,p_3 ,\cdots ,p_m $ 满足\(A_{p_1} =A_{p_2}=\cdots =A_{p_m}\)
将他们统一替换为某个字母,保证\(AB\)均由前20个小写子母构成
$1 \le n \le 10^5 $
题解
将字母建成节点,连接有向边\((A_i,B_i)\) ,得到有向图\(G\)
问题即求最小的边数的新图\(G'\),使得保持\(G\)的连通性
不妨考虑\(G\)是一个弱连通图
设\(D\)是最大DAG子图,则\(ans = 2|G| - 1 - |D|\)
证明
- 设存在一个边数\(k\)的解,构造一个生成树,考虑剩下\(k-n-1\)可能会成环,因此每次至多删掉一个原图节点使为DAG。因此\(|D| \ge n-(k-n-1) = 2n - 1 - k\) 即:\(ans \ge 2n-1-|D|\)
- 考虑对于一个DAG子图D',可以按照拓扑序连成链,然后将非子图的点和链的首位相连,得到一个合法的答案。因此\(ans\le |D'|-1+2(n-|D'|) = 2n-1-|D'|\) 即:\(ans \le 2n-1-|D|\)
- 综上:\(ans=2n-1-|D|\)
通过dp计算最大DAG
复杂度:\(O(N^2+M2^{M})\)
#include<cstdio>
#include<iostream>
using namespace std;
const int N=100010,M=20;
int T,n,a[M],f[1<<M],cnt,ans,b[M][M],vis[M];
char S1[N],S2[N];
void adde(int u,int v){b[u][v]=b[v][u]=1;}
void dfs(int u){
vis[u]=1;
for(int i=0;i<20;++i)if(b[u][i]&&!vis[i])dfs(i);
}
int main(){
// freopen("E.in","r",stdin);
// freopen("E.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%d%s%s",&n,S1,S2);
for(int i=0;i<20;++i){
a[i]=0;
for(int j=0;j<20;++j)b[i][j]=0;
}
for(int i=0;i<n;++i)if(S1[i]!=S2[i]){
a[S1[i]-'a']|=1<<(S2[i]-'a');
adde(S1[i]-'a',S2[i]-'a');
}
ans=cnt=0;
for(int i=0;i<20;++i)vis[i]=0;
for(int i=0;i<20;++i)if(!vis[i]){
cnt++;dfs(i);
}
f[0]=1;
for(int i=1;i<(1<<20);++i){
f[i]=0;
for(int j=0;j<20;++j)if(i&(1<<j)){
f[i] |= f[i^(1<<j)] && ((a[j]&i)==0);
if(f[i])break;
}
if(f[i])ans=max(__builtin_popcount(i),ans);
}
printf("%d\n",40-cnt-ans);
}
return 0;
}//tkys_Austin