AtCoder Regular Contest 077F: SS

http://arc077.contest.atcoder.jp/tasks/arc077_d

実験的にやるのがたぶん正攻法だけど、もうちょっと文字列の周期のこととかに気を配れるとよかった。

解説にも書いてありますが、g(n+2)=g(n+1)+g(n)になります。これは実験をすると比較的簡単に気づけます。
証明は
g(n)→g(n+1)にするということは、g(n)の接頭辞と接尾辞で一致しているもののうち、文字数が最大のものをTとすると、
g(n+1)=g(n)*2-Tとなるので、g(n)=S*n+S'とすれば
g(n+1)=S*n+S'+S(T=S*(n-1)+S'より)
g(n+2)=(S*n+S'+S)+(S*n+S') (T=Sより)
=g(n+1)+g(n)となります。

g(0)=T*n+T'とすると、g(1)=T*n+T'+Tであり、MP法を使えばTは求められるので解けました。
副産物として文字列の長さをNとしてMP[N]=aならば最初のN-a文字が文字列の周期であることもわかりました。


気づいたは気づいたんだけど、証明をどうすればいいかわかりませんでした。
文字列は周期が重要ということを完全に忘れてましたね…。

int N; ll L, R;
int A[MAX_N];
ll C[210][26];
ll D[210];

vector<int> MP(string S) { //it doesn't include itself
	int n = (int)S.size();
	vector<int> A(n + 1);
	A[0] = -1;
	rep(i, 0, n) {
		int j = A[i];
		while (j >= 0 && S[i] != S[j]) j = A[j];
		j++;
		A[i + 1] = j;
	}
	return A;
}

vi calc1(ll n) {
	ll a = n / N;
	ll b = n % N;
	vector<ll> cnt1(26, 0);
	vector<ll> cnt2(26, 0);
	rep(i, 0, N) {
		if(i < b) cnt2[A[i]]++;
		cnt1[A[i]]++;
	}
	rep(i, 0, 26) cnt1[i] = cnt1[i] * a + cnt2[i];
	return cnt1;
}

vi calc2(ll n, int m) {
	vector<ll> res(26, 0);
	for(int i = m - 1; i >= 0; i--) {
		if(n - D[i] >= 0) {
			n -= D[i];
			rep(j, 0, 26) {
				res[j] += C[i][j];
			}
		}
	}
	rep(i, 0, n) res[A[i]]++;
	return res;
}

void solve() {
	string str;
	cin >> str;
	N = sz(str) / 2;
	string tmp;
	rep(i, 0, N) tmp += str[i];
	int a = MP(tmp)[N];

	rep(i, 0, N) A[i] = str[i] - 'a';
	cin >> L >> R; L--;
	if(a == 0) {
		vi lv = calc1(L);
		vi rv = calc1(R);
		rep(i, 0, 26) {
			rv[i] -= lv[i];
			cout << rv[i] << " ";
		}
		cout << "\n";
	}
	else {
		rep(i, 0, N) {
			C[0][A[i]]++; C[1][A[i]]++;
			if(i < N - a) C[1][A[i]]++;
		}
		D[0] = N; D[1] = 2 * N - a;
		int i = 0;
		for(i = 2; ; i++) {
			D[i] = D[i - 1] + D[i - 2];
			rep(j, 0, 26) C[i][j] = C[i - 1][j] + C[i - 2][j];
			if(D[i] >= R) break;
		}
		vi lv = calc2(L, i);
		vi rv = calc2(R, i);
		rep(i, 0, 26) {
			rv[i] -= lv[i];
			cout << rv[i] << " ";
		}
		cout << "\n";
	}
}