Codeforces 314B Sereja and Periods
問題
文字列aをb回繰り返すことを(a, b)と書く。
文字列a, cおよびそれらの繰り返し回数b, dが与えられる。
w = [a, b], q = [c, d]としたとき、
[q, p]がwの(必ずしも連続しない)部分文字列となるような最大の整数pを求めよ。
そのような正の整数pが存在しないときは0を出力せよ。
制約条件
a, cの長さは100以下
b, d≦10^7
方針
[a, b]の繰り返し文字列のうち、aをi回繰り返している時のj文字目を(i, j)と表すことにする。
決まったjから、cを一回マッチさせるごとに、iはいくつ進むかがわかれば、
iがbを超えるまでcをマッチさせ続けて、k回マッチができたとすると、
k / dが答えのpとなる。
毎回のマッチを全部計算すると、答えは10^9くらいになりうるのでTLE.
ここで同じjが出てきたら、前回同じjが出てきたときのiとkから、
差分を見てやって、(b - i) / ⊿i回のループは割り算ですっとばせるので、
すっとばしてやるようにすれば、間に合う。
ソースコード
int b, d; map<int, pi> prev; string a, c; int main(){ cin >> b >> d >> a >> c; int la = a.size(), lc = c.size(); a += a; ll x = 0, y = 0, pa = 0; while(x < b){ if(prev.count(pa)){ ll px = prev[pa].first, py = prev[pa].second; ll t = (b - 1 - x) / (x - px); y += t * (y - py); x += t * (x - px); } else prev[pa] = mp(x, y); int i = 0; y++; for(; i < lc; i++){ for(; pa < 2 * la && a[pa] != c[i]; pa++); if(pa == 2 * la){ cout << 0 << endl; return 0; } if(++pa >= la){ pa -= la; x++; } } //cerr<<x<<" "<<y<<" "<<pa<<endl; } if(pa) y--; cout << y / d << endl; return 0; }