Codeforces 204C. Little Elephant and Furik and Rubik

問題

英大文字からなる長さの等しい文字列a, bが与えられる。
a, bから長さの等しい、空でない連続する部分文字列x, yを取る。


x, yはその全ての候補の中から一つが等しい確率で選ばれる。
f(x, y)を、xi = yi(xのi番目の文字とyのi番目の文字が等しい)なるiの個数とする。
f(x, y)の期待値を求めよ。

制約条件

a, bの長さ≦2 * 10^6

方針

AからZの文字それぞれに対して一致する回数を数え上げる。
これは愚直にやるとO(n^2)かかるが、上手くやるとO(n)で数えられる。


Aの一致回数を数えたいとき、
a XXXAXXXXXXX
b XXXXXAXXXXX

となっていたら、aで、Aを最後に持つ部分文字列は4つ。
bのAに対して、この4つは対応するものがある。

これをどこまで右に伸ばせるかは、bのAの右からの文字数を見ればいい。
それぞれが右に6つまで伸ばせるので、
4 * 6で24通りの一致がある。


つまり、
a XXXXAXXXXX
b XXXXXXXAXX
のように、aのAよりも後ろにあるAについて、一致回数は
Σ(aのAの位置 * bのAの右からの位置)という風に求められるので、
bのAの右からの位置の和を持っておけば全体でO(n)で求められる。


同様にして、
a XXXXXXXAXX
b XXXAXXXXXX
のように、左側にあるAの一致回数については、
aの右側からの位置 * bのAの左からの位置という風に求められる。


あとは一致回数を部分文字列の組み合わせの個数で割ればよい。

ソースコード

int n;
string a, b;

double calc(char c){
	double res = 0, left = 0, right = 0;
	
	rep(i, n) if(b[i] == c) right += n - i;
	rep(i, n){
		if(a[i] == c){
			res += right * (i + 1);
			res += left * (n - i);
		}
		if(b[i] == c){
			right -= n - i;
			left += i + 1;
		}
	}
	return res;
}

void run()
{
	cin >> n;
	cin >> a >> b;
	double ans = 0, sum = 0;
	for(char i = 'A'; i <= 'Z'; i++) ans += calc(i);
	rep(i, n) sum += (i + 1.0) * (i + 1.0);
	
	printf("%.9f\n", ans / sum);
}