25 E Test

問題

文字列s1,s2,s3が与えられる。
s1,s2,s3を(連続する)部分文字列として含むような、長さ最小の文字列の長さを求めよ。

制約条件

s1,s2,s3の長さ≦10^5

方針

文字列A,Bを部分文字列として含む長さ最小の文字列は、

  • AがBに含まれる、またはBがAに含まれる場合、AあるいはBそのもの
  • そうでないとき、Aの末尾i文字とBの先頭i文字が一致する場合、それを重ねたもの


なので、s1,s2,s3を、最大何字末尾と先頭が重なるかを見てやればよい。
これはナイーブにやるとO(n^2)かかってTLEになるが、
rolling hashを使えばO(n)になるので通る。


rolling hashは何回か書いたが、文字列sをc1,c2,c3,c4,...,cnとするとき、
c1c2c3c4...cnを27進法の数字として見た値のことである。

ソースコード

string in[3];
ll ha[200001],hb[200001];
string merge(string &a,string &b){
	int la=a.size(), lb=b.size(), ret=0;
	ll tmp;
	
	if(la>=lb){
		ha[0]=hb[0]=0;
		tmp=1; rep(i,la)ha[i+1]=ha[i]+(a[i]-'a')*tmp, tmp*=29;
		tmp=1; rep(i,lb)hb[i+1]=hb[i]+(b[i]-'a')*tmp, tmp*=29;
		
		tmp=hb[lb];
		for(int i=lb;i<=la;i++){
			if(ha[i]-ha[i-lb]==tmp)return a;
			tmp*=29;
		}
	}
	
	ha[la]=hb[0]=0;
	rep(i,la)ha[la-i-1]=ha[la-i]*29+(a[la-i-1]-'a');
	tmp=1; rep(i,lb)hb[i+1]=hb[i]+(b[i]-'a')*tmp, tmp*=29;
	
	for(int i=1;i<=min(la,lb);i++){
		if(ha[la-i]==hb[i])ret=i;
	}
	return a.substr(0,la-ret)+b;
}

void run()
{
	rep(i,3)cin>>in[i];
	sort(in,in+3);
	int ans=1e9;
	do{
		string a,b;
		a=merge(in[0],in[1]);
		b=merge(a,in[2]);
		ans=min(ans,(int)b.size());
	}while(next_permutation(in,in+3));
	
	cout<<ans<<endl;
}