https://www.ioi-jp.org/camp/2014/2014-sp-tasks/2014-sp-d3.pdf
(問題PDFのURL)
概要
J, O, I からなる長さNの文字列が与えられる。この文字列の(連続する)部分文字列の中で、J, O, Iの数が等しい部分文字列の最大の長さを求めよ。
制約
N ≦ 2*10^5
方針
str[s : t] :文字列strのs文字目からt文字目の部分文字列(len(str[s : t]) = t - s + 1)
J[t], O[t], I[t]:それぞれ、1文字目からt文字目までのJの数、Oの数、Iの数 とする。
str[s : t]のJ, O, Iの数が等しい ⇔ J[t] - J[s-1] = O[t] - O[s-1] かつ J[t] - J[s-1] = I[t] - I[s-1]
よって、愚直にはs, tの全探索で上記の条件を満たすt, sについて、t - s + 1の最大値を出力する。
ただし、これだとO(N^2)となり、間に合わない。
J[t] - J[s-1] = O[t] - O[s-1] かつ J[t] - J[s-1] = I[t] - I[s-1] … Aを
J[t] - O[t] = J[s-1] - O[s-1] かつ J[t] - I[t] = J[s-1] - I[s-1] と移行する。
a[t] = J[t] - O[t], b[t] = J[t] - I[t] とおくと、Aの条件は
a[t] = a[s-1] かつ b[t] = b[s-1] … B
と書き換えることができる。
Bの条件を満たす最長のt - s + 1は尺取り法を用いた以下の手順でO(NlogN)で計算することができる。
- a, b, idxをセットにした構造体を定義する(pos)
- a, b, idxの優先順に昇順ソートする
- x, yを準備し、pos[x].a = pos[y].a, pos[x].b = pos[y]となる間yを進める。その間pos[y].idx - pos[x].idxの最大値を更新する。
- (pos[x].a = pos[y].a, pos[x].b = pos[y])でなくなったら、xをyとする。
解答
模範解答のコピペ。。
Submission #9593351 - 2014年 日本情報オリンピック春合宿 オンラインジャッジ
/* Sort then Shakutori (O(N log N)) */ #include <bits/stdc++.h> using namespace std; struct Pos { int jo; int ji; int len; bool operator < (const Pos &other) const { if (jo != other.jo) return jo < other.jo; if (ji != other.ji) return ji < other.ji; return len < other.len; } }; int chmax(int &a, int n) { if (n > a) a = n; return a; } int main() { int N; string S; cin >> N >> S; vector<Pos> ois; Pos origin; origin.jo = origin.ji = origin.len = 0; ois.push_back(origin); int Js = 0, Os = 0, Is = 0; for (int i = 0; i < N; i++) { char c = S[i]; if (c == 'J') Js++; else if (c == 'O') Os++; else Is++; Pos pos; pos.jo = Js - Os; pos.ji = Js - Is; pos.len = i + 1; ois.push_back(pos); } sort(ois.begin(), ois.end()); int ans = 0; int lastind = 0; for (int i = 1; i < ois.size(); i++) { if (ois[i].jo == ois[lastind].jo && ois[i].ji == ois[lastind].ji) { chmax(ans, ois[i].len - ois[lastind].len); } else { lastind = i; } } cout << ans << endl; return 0; }