Codeforces 120 H. Brevity is Soul of Wit
問題
文字列の、「4文字以下の(必ずしも連続しない)部分文字列」をその文字の略称にできるとする。
与えられたn個の文字列の略称を、重複なく定められるかどうかを判定せよ。
重複なく定められる場合、その略称をどれか一組出力し、
そうでない場合-1を出力せよ。
制約条件
n≦200
方針
文字列および、略称を頂点として、対応しうる略称に辺を引いたグラフを考える。
これは二部グラフであり、重複なく略称が定められるかは、このグラフが大きさnのマッチングを持つことに同値である。
したがって、グラフを作成し、二部グラフの最大マッチングを求めればよい。
グラフをあんまりナイーブに作成するとTLEになった。
どの文字列の略称にもなりえない文字列は生成しないようにし、
略称が対応するかを、使われている文字種で枝刈りしたら通った。
ソースコード
int n,m; string in[200]; vi g[500000]; vector<string> list; int p[500000],use[200]; bool v[500000]; bool match(int s){ if(s<0)return 1; rep(i,g[s].size())if(!v[g[s][i]]){ v[g[s][i]]=1; if(match(p[g[s][i]]))return p[g[s][i]]=s, p[s]=g[s][i], 1; } return 0; } bool ok(string &a,string &b){ int n=a.size(), m=b.size(); for(int i=0,j=0;i<n;i++){ if(a[i]==b[j])if(++j>=m)return 1; } return 0; } void gen(string s,int bit){ if(!s.empty()){ bool any=0; rep(i,n)if((~use[i]&bit)==0&&ok(in[i],s)){ any=1; g[i].pb(list.size()+n); g[list.size()+n].pb(i); } if(any)list.pb(s); else return; } if(s.size()<4)for(char c='a';c<='z';c++){ string t=s+c; gen(t,bit|1<<c-'a'); } } void run() { cin>>n; rep(i,n){ cin>>in[i]; use[i]=0; rep(j,in[i].size())use[i]|=1<<in[i][j]-'a'; } list.clear(); gen("",0); m=list.size(); int cnt=0; memset(p,-1,sizeof(p)); rep(i,n){ rep(j,n+m)v[j]=0; if(p[i]<0) if(match(i))cnt++; } if(cnt<n)cout<<-1<<endl; else rep(i,n)cout<<list[p[i]-n]<<endl; }