結果
問題 | No.515 典型LCP |
ユーザー | shugo256 |
提出日時 | 2020-02-03 23:44:56 |
言語 | C++14 (gcc 12.3.0 + boost 1.83.0) |
結果 |
TLE
|
実行時間 | - |
コード長 | 9,412 bytes |
コンパイル時間 | 1,283 ms |
コンパイル使用メモリ | 101,304 KB |
実行使用メモリ | 48,124 KB |
最終ジャッジ日時 | 2024-09-19 11:40:17 |
合計ジャッジ時間 | 14,083 ms |
ジャッジサーバーID (参考情報) |
judge4 / judge5 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | TLE | - |
testcase_01 | TLE | - |
testcase_02 | WA | - |
testcase_03 | AC | 1 ms
6,940 KB |
testcase_04 | AC | 2 ms
6,940 KB |
testcase_05 | WA | - |
testcase_06 | WA | - |
testcase_07 | WA | - |
testcase_08 | WA | - |
testcase_09 | WA | - |
testcase_10 | WA | - |
testcase_11 | WA | - |
testcase_12 | WA | - |
testcase_13 | WA | - |
testcase_14 | WA | - |
testcase_15 | WA | - |
testcase_16 | WA | - |
ソースコード
#include <iostream> #include <algorithm> #include <vector> #include <queue> #include <string> using ll = long long; using namespace std; #define isLMS(i) (isL[(i)-1] && !isL[i]) #define LIMIT 200000 template <typename T> class SuffixArray { vector<T> s; const size_t len, limit; // lenは配列長 limitは配列に登場する文字/数字の種類数 vector<size_t> bin, // 頭文字の各アルファベットの出現回数の累積和 cnt, // どのアルファベットから始まるものをいくつsaに割り当てたか lms, // LMSのインデックスを格納する配列 isL, // s[i]がL型なら1 S型なら0 sa; // sのsuffix arrayをインデックスで格納する配列 public: vector<size_t> rank; // s[i:]が辞書順で何番目のsuffixか(saの逆写像)、番兵も // Tが整数の時よう SuffixArray(vector<T> s, size_t limit=LIMIT) : s(s), len(s.size()), limit(limit), bin(limit+1, 0), cnt(limit+1, 0), isL(len+1), sa(len+1), rank(len+1) { (this->s).push_back(0); // 末尾に番兵の追加 lms.reserve(len/2); // lmsの領域の確保 sa_is(); // SAの構築 for (size_t i=0; i<=len; i++) rank[sa[i]] = i; // rankの計算 } // Tがcharの時用 SuffixArray(string s, size_t limit=256) : SuffixArray(vector<char>(s.begin(), s.end()), limit) {} // LCP(高さ配列、蟻本p339)を計算する // lcp[i] = sa[i]とsa[i+1]の先頭何文字が一致しているか // このlcpをRange Minimum Queryに載せれば任意のi jに対してlcpが計算可能 vector<int> lcp; void construct_lcp() { lcp.resize(len); size_t h = 0; for (size_t i=0; i<len; i++) { size_t j = sa[rank[i] - 1]; if (h > 0) h--; for ( ; i+h<len && j+h<len && s[i+h] == s[j+h]; h++); lcp[rank[i] - 1] = (int)h; } } auto begin() { return sa.begin(); } auto end() { return sa.end(); } template <typename ID> int operator[](ID i) { return (int)sa[(size_t)i]; } private: //SA-IS法によりSuffixArrayを構築する void sa_is() { isL[len] = false; bin[1] = 1; for (size_t i=len; i>0; i--) { isL[i-1] = (s[i-1] == s[i]) ? isL[i] : (s[i-1] > s[i]); bin[(size_t)s[i-1]+1]++; if (isLMS(i)) lms.push_back(i); } vector<size_t> aligned(lms); // あとでlmsは色々並べ替えるが、ここに今の整列した状態を保存しておく reverse(aligned.begin(), aligned.end()); // 降順になっているのをひっくり返して昇順にする for (size_t i=0; i<limit; i++) bin[i+1] += bin[i]; // 累積和 induced_sort(); // 1回目のinduced sort fill(sa.begin(), sa.end(), 0); // saにLMS部分文字列の順位を格納する size_t n = 0, lmslen = lms.size(); // 辞書順で隣り合うLMSについて一致しているかどうか調べる(たかだかn回の比較) for (auto itr=lms.begin(); itr+1!=lms.end(); itr++) { size_t i = *itr, j = *(itr+1); for (size_t d=0; i+d <= len && j+d <= len; d++) { if (s[i+d] != s[j+d] || isLMS(i+d) != isLMS(j+d)) { n++; break; } else if (d > 0 && (isLMS(i+d) || isLMS(j+d))) break; } sa[j] = n; } if (n < lmslen-1) { vector<size_t> next_s(lmslen-1); for (size_t i=0; i<lmslen-1; i++) { // alignedはここで使うために用意した next_s[i] = sa[aligned[i]]; sa[aligned[i]] = 0; // saはまた0リセット } // sa-isを再帰的に用いる SuffixArray<size_t> next(next_s, n+1); for (size_t i=0; i<lmslen-1; i++) lms[i] = aligned[(size_t)next[lmslen-1-i]]; lms[lmslen-1] = len; } else { for (auto i:aligned) sa[i] = 0; // saを0リセット } fill(cnt.begin(), cnt.end(), 0); induced_sort(); } // induced sortの3ステップを行う void induced_sort() { // step1:LMSをひとまず書き込んでいく for (auto i:lms) { size_t c = (size_t)s[i]; cnt[c+1]++; sa[bin[c+1] - cnt[c+1]] = i; } fill(cnt.begin(), cnt.end(), 0); //step2:L型のsuffixを埋めていく for (auto i:sa) { if (i == 0 || !isL[i-1]) continue; size_t c = (size_t)s[i-1]; sa[bin[c] + cnt[c]] = i-1; cnt[c]++; } fill(cnt.begin(), cnt.end(), 0); auto lmsitr = lms.rbegin(); //step3:S型のsuffixを埋めていく for (auto itr=sa.rbegin(); itr!=sa.rend(); itr++) { size_t i = *itr; if (i == 0) continue; if (isLMS(i)) { // ここでlmsをsaに出てくる順に並べ替えておく *lmsitr = i; lmsitr++; } if (isL[i-1]) continue; size_t c = (size_t)s[i-1]; cnt[c]++; sa[bin[c+1] - cnt[c]] = i-1; } } }; // 番兵を0としているので、sの要素は全部正にする(or番兵を変える) #include <functional> #include <limits> #define ASSIGN_I [](T &ival, T x) { ival = x; } // i番目にxを代入(点更新のデフォルト) template <typename T> class SegmentTree { int n; // 葉の数 T def; // 初期値 && 単位元 vector<T> tree; // 本体 using op_func_t = function<T(T, T)>; using up_func_t = function<void(T&, T)>; op_func_t op_func; // 区間クエリで使う処理 up_func_t up_func; // 点更新で使う処理 ただの変更の他、i番目にxをたすみたいなこともできる // 区間[a, b)の総和(と言うか総operation(は?)) // ノードk=[l, r)を見ている T _query(int a, int b, int k, int l, int r) { if (r <= a || b <= l) return def; // 全く交差しない場合 if (a <= l && r <= b) return tree[(size_t)k]; // 完全に包含される場合 T vl = _query(a, b, k * 2 + 1, l, (l + r) / 2), vr = _query(a, b, k * 2 + 2, (l + r) / 2, r); return op_func(vl, vr); } public: SegmentTree(int _n, T _def, op_func_t _op_func, up_func_t _up_func = ASSIGN_I) : def(_def), op_func(_op_func), up_func(_up_func) { n = 1; while (n < _n) n <<= 1; tree = vector<T>((size_t)(2*n - 1), def); } //よく使うやつら min, max, sum SegmentTree(int _n, T _def, string mode, up_func_t _up_func = ASSIGN_I) : SegmentTree( _n, _def, mode == "max" ? [](T l, T r) { return max(l, r); } : (mode == "min" ? [](T l, T r) { return min(l, r); } : [](T l, T r) { return l + r; }), // sum _up_func ) {} SegmentTree(int _n, string mode, up_func_t _up_func = ASSIGN_I) : SegmentTree( _n, mode == "max" ? numeric_limits<T>::lowest() : (mode == "min" ? numeric_limits<T>::max() : 0), // sum mode, _up_func ) {} template <typename ID> void update(ID i, T x) { i += (ID)n - 1; up_func(tree[(size_t)i], x); while (i > 0) { i = (i - 1) / 2; tree[(size_t)i] = op_func(tree[(size_t)(i * 2 + 1)], tree[(size_t)(i * 2 + 2)]); } } template <typename ID1, typename ID2> T query(ID1 a, ID2 b) { return _query((int)a, (int)b, 0, 0, n); } void print_tree() { size_t next = 0, size = (size_t)(2 * n - 1); for (size_t i=0; i<size; i++) { cout << tree[i]; if (i == next) { cout << '\n'; next = (next + 1) * 2; } else cout << string(size * 2 / (next+2), ' '); } } auto begin() { return tree.begin() + n - 1; } auto end() { return tree.end(); } T operator[](int i) { return tree[ (size_t)(i + n - 1) ]; } }; /* コンストラクタ SegmentTree(n, def, op_func, [up_func]) SegmentTree(n, def, mode, [up_func]) SegmentTree(n, mode, [up_func]) */ int main() { ll n; cin >> n; string s, si; int tops[n+1]; for (int i=0; i<n; i++) { cin >> si; tops[i] = s.size(); s += si; } tops[n] = s.size(); SuffixArray<char> sa(s); sa.construct_lcp(); SegmentTree<int> sgt(s.size(), "min"); int l=0; for (auto &lcpl:sa.lcp) sgt.update(l++, lcpl); int m; ll x, d, ans = 0; cin >> m >> x >> d; for (int k=0; k<m; k++) { int i = (x / (n-1)), j = (x % (n-1)); if (i > j) swap(i, j); else j++; x = (x + d) % (n * (n-1)); ans += min({sgt.query(sa.rank[tops[i]], sa.rank[tops[j]]), tops[i+1] - tops[i], tops[j+1] - tops[j]}); } cout << ans << '\n'; return 0; }