kmjp's blog

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

Codeforces ECR #030 : F. Forbidden Indices

これ系は一度解いておきたかったので解けて良かった。
http://codeforces.com/contest/873/problem/F

問題

N文字の文字列Sと、N bitのbitmapが与えられる。
ある文字列Aに対し、f(A)をSの部分文字列としてAが登場する回数のうち、末尾の文字の位置に対応するbitが0のものとする。

A f(A)の最大値を求めよ。

解法

Sとbitmapを反転させると、bitの0/1は末尾でなく先頭だけ考慮すればよくなる。

この状態でSuffixArrayを求めよう。
ここで(prefixが一致するの文字数)×(該当する文字の先頭位置のうちbitが0のもの)の最大値を求めたい。

SA中i番目の要素と(i+1)番目の要素のLCPをLCP[i]とするとき、LCP[L]~LCP[R]の最小値がLCP[i]と一致する最小のLと最大のRを求めよう。
これはRMQ+二分探索などで求められる。
すると上記式はLCP[i]*(L~R+1番目の先頭位置のうちbitが0のもの)となるので後者をBIT等で管理すれば高速に処理できる。

struct SuffixArray {
	int N; vector<int> rank,lcp,sa; string S;
	
	SuffixArray(string S) : S(S){
		int i,h=0; vector<int> tmp,tr;
		N=S.size(); rank.resize(N+1); sa.resize(N+1); tmp.resize(N+1);
		FOR(i,N+1) sa[i]=i, rank[i]=i==N?-1:S[i];
		
		for(int k=1; k<=N; k<<=1) {
			auto pred2 = [k,this](int& a,int &b)->bool{ return (((a+k<=N)?rank[a+k]:-1)<((b+k<=N)?rank[b+k]:-1));};
			auto pred = [pred2,k,this](int& a,int &b)->bool{ return (rank[a]!=rank[b])?(rank[a]<rank[b]):pred2(a,b);};
			int x=0;
			if(k!=1) for(i=1;i<N+1;i++) if(rank[sa[i]]!=rank[sa[x]]) sort(sa.begin()+x,sa.begin()+i,pred2), x=i;
			sort(sa.begin()+x,sa.end(),pred);
			FOR(i,N+1) tmp[sa[i]]=(i==0)?0:tmp[sa[i-1]]+pred(sa[i-1],sa[i]);
			swap(rank,tmp);
		}
		lcp.resize(N+1); tr.resize(N+1);
		FOR(i,N+1) tr[sa[i]]=i;
		FOR(i,N) {
			int j=sa[tr[i]-1];
			for(h=max(h-1,0);i+h<N && j+h<N; h++) if(S[j+h]!=S[i+h]) break;
			lcp[tr[i]-1]=h;
		}
	}
};

template<class V, int ME> class BIT {
public:
	V bit[1<<ME];
	V operator()(int e) {if(e<0) return 0;V s=0;e++;while(e) s+=bit[e-1],e-=e&-e; return s;}
	void add(int e,V v) { e++; while(e<=1<<ME) bit[e-1]+=v,e+=e&-e;}
};
BIT<int,20> bt;

template<class V,int NV> class SegTree_1 {
public:
	vector<V> val;
	static V const def=1<<20;
	V comp(V l,V r){ return min(l,r);};
	
	SegTree_1(){val=vector<V>(NV*2,0);};
	V getval(int x,int y,int l=0,int r=NV,int k=1) { // x<=i<y
		if(r<=x || y<=l) return def;
		if(x<=l && r<=y) return val[k];
		return comp(getval(x,y,l,(l+r)/2,k*2),getval(x,y,(l+r)/2,r,k*2+1));
	}
	void update(int entry, V v) {
		entry += NV;
		val[entry]=v;
		while(entry>1) entry>>=1, val[entry]=comp(val[entry*2],val[entry*2+1]);
	}
};
SegTree_1<int,1<<20> st;
int N;
string S,T;

int LCP[201010];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N>>S>>T;
	reverse(ALL(S));
	reverse(ALL(T));
	SuffixArray SA(S);
	
	ll ma=0;
	for(i=1;i<=N;i++) {
		LCP[i]=SA.lcp[i];
		st.update(i,LCP[i]);
		bt.add(i,T[SA.sa[i]]=='0');
		if(T[SA.sa[i]]=='0') ma=max(ma,(ll)(N-SA.sa[i]));
	}
	
	for(i=1;i<=N;i++) if(LCP[i]>0) {
		int R=i,L=i;
		for(j=18;j>=0;j--) if(st.getval(i,R+(1<<j))>=LCP[i]) R+=1<<j;
		for(j=18;j>=0;j--) if(L-(1<<j)>=0 && st.getval(L-(1<<j),i)>=LCP[i]) L-=1<<j;
		ma=max(ma,1LL*LCP[i]*(bt(R)-bt(L-1)));
	}
	cout<<ma<<endl;
	
}

まとめ

SuffixArrayにおけるprefixが一致する最大面積、初めてまともに取り扱ったけど解けて良かった。