持ってたネタと被った。ABCでFFT出るのね。
https://atcoder.jp/contests/abc196/tasks/abc196_f
問題
01で構成された文字列S,Tが与えられる。
Tの文字をいくつか置き換え、TがSの部分文字列となるようにしたい。
最小何文字置き換えればよいか。
解法
S中の|T|文字と、Tが何文字一致するかを、開始位置に応じて網羅的に求める。
そのためFFTを使う。
多項式P(x),Q(x)を以下のように定める。
P(x) := xのn次の係数は、S[n]が0なら-1、1なら1
Q(x) := xのn次の係数は、T[|T|-1-n]が0なら-1、1なら1
P(x)*Q(x)のxの(n+m)次の係数を考えると、Pのn次の項とQのm次の項が一致する、すなわちS[n]=T[|T|-1-m]なら1、そうでないなら-1が形状される。
よって、xの(n+m)次の係数をRとすると、(|T|-R)/2が不一致な文字の数となる。
よってP(x)*Q(x)をFFTで求め、各次数の係数を見ていこう。
typedef complex<double> Comp; vector<Comp> fft(vector<Comp> v, bool rev=false) { int n=v.size(),i,j,m; for(i=0,j=1;j<n-1;j++) { for(int k=n>>1;k>(i^=k);k>>=1); if(i>j) swap(v[i],v[j]); } for(int m=2; m<=n; m*=2) { double deg=(rev?-1:1) * 2*acos(-1)/m; Comp wr(cos(deg),sin(deg)); for(i=0;i<n;i+=m) { Comp w(1,0); for(int j1=i,j2=i+m/2;j2<i+m;j1++,j2++) { Comp t1=v[j1],t2=w*v[j2]; v[j1]=t1+t2, v[j2]=t1-t2; w*=wr; } } } if(rev) FOR(i,n) v[i]*=1.0/n; return v; } vector<Comp> MultPoly(vector<Comp> P,vector<Comp> Q,bool resize=false) { if(resize) { int maxind=0,pi=0,qi=0,i; int s=2; FOR(i,P.size()) if(norm(P[i])) pi=i; FOR(i,Q.size()) if(norm(Q[i])) qi=i; maxind=pi+qi+1; while(s*2<maxind) s*=2; P.resize(s*2);Q.resize(s*2); } P=fft(P), Q=fft(Q); for(int i=0;i<P.size();i++) P[i]*=Q[i]; return fft(P,true); } string S,T; void solve() { int i,j,k,l,r,x,y; string s; vector<Comp> A,B; cin>>S>>T; A.resize(1<<21); B.resize(1<<21); FOR(i,S.size()) { if(S[i]=='0') A[i]=-1; else A[i]=1; } FOR(i,T.size()) { if(T[i]=='0') B[T.size()-1-i]=-1; else B[T.size()-1-i]=1; } auto C=MultPoly(A,B); int ma=-1<<30; for(i=T.size()-1;i<S.size();i++) ma=max(ma,(int)round(C[i].real())); cout<<(T.size()-ma)/2<<endl; }
まとめ
ほぼ同じ問題を考えていたので、使えなくなっちゃったな。
せっかくだから設定を書いておくと、こんな感じ。
N人の男子の列と、N人の女子の列があり、それぞれ利き手が与えられる。 両列で同じ位置の男女をペアにし、N組のペアを作ってダンスを踊る。 利き手が異なるペアは、互いに利き手を使って手をつなぐので見栄えが良くなる。 それぞれの列を任意にrotateできるとき、見栄えの良いペアの最大何ペア?