kmjp's blog

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

yukicoder : No.515 典型LCP

想定解じゃないのが通りまくっている様子だが、チャレンジケース追加で通らなくなった。
http://yukicoder.me/problems/no/515

問題

N個の文字列S[i]が与えられる。
M個のクエリ(i,j)が与えられるので、それぞれS[i],S[j]の最長共通接頭辞の長さの総和を求めよ。

解法

チャレンジケース追加前はローリングハッシュで通ったが、追加後はそれでは通らなくなった。

事前にS[x]をソートしておいて隣接要素同士のLCPを求めておこう。
各クエリはS[i]およびS[j]がソート後a,b番目だったとする。以下a<bとする。
その場合、a,a+1,a+2,....,b番目の文字列のLCPを求めることと等しい。

これは(aとa+1)、(a+1とa+2)...(b-1,b)のLCP長の最小値となる。
よってRMQの問題に帰結できる。
あとは各クエリをSegTreeまたはSparse Treeで処理していく。
クエリが重いので後者の方がよさそう。

ll N;
string S[101010];
pair<string,int> P[101010];
int ID[101010];
ll M,X,D;

int com[101010][20];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N;
	FOR(i,N) {
		cin>>S[i];
		P[i]={S[i],i};
	}
	sort(P,P+N);
	FOR(i,N) ID[P[i].second]=i;
	FOR(i,N-1) {
		x=P[i].second;
		y=P[i+1].second;
		FOR(r,min(S[x].size(),S[y].size())) if(S[x][r]!=S[y][r]) break;
		com[i][0]=r;
	}
	for(x=1;x<=19;x++) FOR(i,N-1) if(i+(1<<(x-1))<N) com[i][x]=min(com[i][x-1],com[i+(1<<(x-1))][x-1]);
	
	ll ret=0;
	cin>>M>>X>>D;
	for(int k=1;k<=M;k++) {
		x=X/(N-1);
		y=X%(N-1);
		if(x>y) swap(x,y);
		else y++;
		X = (X+D) % (N*(N-1));
		
		x=ID[x];
		y=ID[y];
		if(x>y) swap(x,y);
		
		r=0;
		while(1<<(1+r)<=y-x) r++;
		
		ret += min(com[x][r],com[y-(1<<r)][r]);
		
	}
	cout<<ret<<endl;
}

まとめ

Sparse Tableはまだこれで2回しか使ったことがない。
なので解法が頭に浮かばない…。