kmjp's blog

競技プログラミング参加記です

yukicoder : No.2162 Copy and Paste 2

コピペ問題は毎回苦労する。
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とかで出たらさっくり解ける気しないなぁ。