コピペ問題は毎回苦労する。
https://yukicoder.me/problems/no/2162
問題
文字列T,Uがあり、それぞれ初期値は空である。
以下の処理を繰り返し、Tを与えられた文字列Sに一致させたい。
最小何回の処理が必要か。
- Tの末尾に'a'を追加する
- Tの末尾に'b'を追加する
- UにTを代入する
- Tの末尾にUを追加する
解法
dp(i,j) := TをSの先頭i文字、UをSの先頭j文字とするのにかかる最小処理回数
とする。
dp2(i,j) := i-dp(i,j)
dp3(i) = max(dp2(i,j))
とすると、求めたいのはdp3(N)である。
jをインクリメントさせながら、dp2(i,j)を埋めて行こう。
dp2(i,j)の状態で4つ目の処理を行うと、dp2(i+j,j)~dp2(N,j)までがdp2(i,j)+j-1で更新される。
これを順次処理しよう。
dp2(i,j)すべてにおいて上記処理を行うとO(N^2)かかるが、dp2(i,j)が更新された場合のみ行うことで、処理回数を削減できる。
vector<int> Zalgo(string s) { vector<int> v(1,s.size()); for(int i=1,l=-1,r=-1;i<s.size();i++) { if(i<=r && v[i-l]<r-i+1) v.push_back(v[i-l]); else { l=i; r=(i>r)?i:(r+1); while(r<s.size() && s[r-i]==s[r]) r++; v.push_back((r--)-l); } } v.push_back(0); return v; } template<class V,int NV> class SegTree_2 { public: V nolink; vector<V> val; SegTree_2(){val.resize(NV*2); clear(); nolink=-100;}; void clear() { for(int i=0;i<NV*2;i++) val[i]=0; } V getval(int k) { int e=k+NV; V ret=nolink; while(e>=1) ret=max(ret,val[e]), e/=2; return ret; } void update(int x,int y, V v,int l=0,int r=NV,int k=1) { if(l>=r) return; if(x<=l && r<=y) val[k]=max(val[k],v); else if(l < y && x < r) { update(x,y,v,l,(l+r)/2,k*2); update(x,y,v,(l+r)/2,r,k*2+1); } } }; int N; string S; set<int> alive; SegTree_2<int,1<<20> st; vector<int> del[202020]; void solve() { int i,j,k,l,r,x,y; string s; cin>>S; N=S.size(); auto Z=Zalgo(S); for(i=1;i<=N-1;i++) { Z[i]=min(i,Z[i]); if(Z[i]>1) { alive.insert(i); del[Z[i]].push_back(i); } } alive.insert(N+1); for(i=2;i<N;i++) { int cv=st.getval(i)+i-2; int nex=*alive.begin()+i; while(nex<=N) { st.update(nex,N+1,cv); cv+=i-1; nex=*alive.lower_bound(nex)+i; } FORR(e,del[i]) alive.erase(e); } cout<<N-st.getval(N)<<endl; }
まとめ
これARCとかで出たらさっくり解ける気しないなぁ。