传送门:点击打开链接
题意:给出两个等长的字符串s1和s2,对字母表进行重新排列,若s1[i]和s2[i]在原字母表和新字母表中的位置相同则得分+1,求字母表的一种重排方式使得分最大
思路:字母表重排实际上是一个完全匹配问题,并且可知边权w(x,y)是两个字符串中出现s1[i]=x&&s2[i]=y的总次数,于是就变成了一个最优匹配问题,直接上KM模块
代码:
#include<iostream>
#include<string.h>
#include<stdio.h>
#define INF 1000000000
using namespace std;
int w[55][55],lx[55],ly[55],a[55];
int mx[55],my[55],d;
bool vx[55],vy[55];
char s1[2000005],s2[2000005];
bool dfs(int x,int n){
vx[x]=true;
for(int y=0;y<n;y++){
if(!vy[y]){
if(lx[x]+ly[y]==w[x][y]){
vy[y]=true;
if(my[y]==-1||dfs(my[y],n)){
mx[x]=y;
my[y]=x;
return true;
}
}
else{
d=min(d,lx[x]+ly[y]-w[x][y]);
}
}
}
return false;
}
void KM(int m,int n){
memset(mx,-1,sizeof(mx));
memset(my,-1,sizeof(my));
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
lx[i]=max(lx[i],w[i][j]);
}
}
for(int k=0;k<m;k++){
memset(vx,false,sizeof(vx));
memset(vy,false,sizeof(vy));
d=INF;
if(dfs(k,n)) continue;
for(int i=0;i<m;i++) if(vx[i]) lx[i]-=d;
for(int i=0;i<n;i++) if(vy[i]) ly[i]+=d;
k--;
}
}
int getnum(char c){
if(c>='a'&&c<='z') return c-'a';
return c-'A'+26;
}
char getc(int x){
if(x>=0&&x<26) return x+'a';
return x-26+'A';
}
int main(){
int n,k;
scanf("%d%d",&n,&k);
scanf("%s%s",s1,s2);
for(int i=0;i<n;i++){
int p=getnum(s1[i]),q=getnum(s2[i]);
w[p][q]++;
}
KM(k,k);
int ans=0;
for(int i=0;i<k;i++){
ans+=w[i][mx[i]];
}
printf("%d\n",ans);
for(int i=0;i<k;i++){
printf("%c",getc(mx[i]));
}
return 0;
}
或者可以用费用流来做,不过稠密图费用流的速度要比O(n4)的KM还要慢
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
#define MAXN 10100
#define MAXM 100000
#define INF (1<<30)
struct edge{
int u,v,cap,cost,next;
};
edge e[MAXM];
int s,t;
int cnt,nn,m,sumflow;
int head[MAXN],dis[MAXN],pre[MAXN];
bool vis[MAXN];
void insert(int u,int v,int cap,int cost){
e[cnt].u=u;e[cnt].v=v;e[cnt].cap=cap;e[cnt].cost=cost;
e[cnt].next=head[u];head[u]=cnt++;
e[cnt].u=v;e[cnt].v=u;e[cnt].cap=0;e[cnt].cost=-cost;
e[cnt].next=head[v];head[v]=cnt++;
}
bool spfa(){
int i,u,v;
int que[MAXN*2],front=1,rear=0;
memset(vis,false,sizeof(vis));
memset(pre,-1,sizeof(pre));
for(i=0;i<=nn+1;i++){
dis[i]=INF;
}
vis[s]=true;
dis[s]=0;
que[++rear]=s;
while(front<=rear){
u=que[front++];
vis[u]=false;
for(i=head[u];i!=-1;i=e[i].next){
v=e[i].v;
if(e[i].cap&&dis[v]>dis[u]+e[i].cost){
dis[v]=dis[u]+e[i].cost;
pre[v]=i;
if(!vis[v]){
que[++rear]=v;
vis[v]=true;
}
}
}
}
if(dis[t]==INF) return false;
return true;
}
int MCMF(){
int flow=0;
int i,minflow,mincost;
mincost=0;
while(spfa()){
minflow=INF;
for(i=pre[t];i!=-1;i=pre[e[i].u]){
if(e[i].cap<minflow) minflow=e[i].cap;
}
flow+=minflow;
for(i=pre[t];i!=-1;i=pre[e[i].u]){
e[i].cap-=minflow;
e[i^1].cap+=minflow;
}
mincost+=dis[t]*minflow;
}
sumflow=flow;
return mincost;
}
char s1[2000005],s2[2000005];
int getnum(char c){
if(c>='a'&&c<='z') return c-'a'+1;
return c-'A'+27;
}
char getc(int x){
if(x>=0&&x<26) return x+'a';
return x-26+'A';
}
int res[60];
int w[60][60];
int main(){
int u,n,v,c;
int i,j,k;
scanf("%d%d",&n,&k);
scanf("%s%s",s1,s2);
for(int i=0;i<n;i++){
int p=getnum(s1[i]),q=getnum(s2[i]);
w[p][q]++;
}
s=0;t=2*k+1;
nn=2*k;
cnt=0;
memset(head,-1,sizeof(head));
for(i=1;i<=k;i++){
for(j=1;j<=k;j++){
insert(i,j+k,1,-w[i][j]);
}
}
for(i=1;i<=k;i++){
insert(0,i,1,0);
insert(i+k,t,1,0);
}
int ans=MCMF();
printf("%d\n",-ans);
for(i=0;i<cnt;i++){
if(e[i].cap==0){
res[e[i].u]=e[i].v-k;
}
}
for(i=1;i<=k;i++){
printf("%c",getc(res[i]-1));
}
return 0;
}