UVa12238
接尾辞配列ライクなことをやる。
与えられた文字列をソートし、となりあう文字列の一致する最長のプレフィックス (LCP) を求める。
それに RMQ を使ってクエリーを処理する。
A[i] と A[i+1] のプレフィックスが n 文字目まで一致していて、
A[i+1] と A[i+2] のプレフィックスが m 文字目まで一致していれば、
A[i] と A[i+2] は min(n, m) 文字目までのプレフィックスが一致していることになる。
A[j]とA[k]のプレフィックスはRMQ(j, k)文字目まで一致することになる。
このアプローチで実行時間が3秒強。タイムリミットが5秒。
ラディックスソートとか使えばもっと早くなるかも?
RMQはスパソ式。
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#define REP(i, n) for(int i=0; i<(int)n; ++i)
#define FOR(i, c) for(__typeof((c).begin())i=(c).begin();i!=(c).end();++i)
#define ALL(c) (c).begin(),(c).end()
typedef long long int lli;
using namespace std;
class RMQ {
public:
int *n, size;
RMQ()
{
n = NULL;
}
RMQ(int a[], int s)
{
build(a, s);
}
void build(int a[], int s)
{
int lg = 1;
size = s;
for (int i = 1; i < s; i *= 2) ++lg;
int *m = n = new int[s * lg];
copy(a, a + s, m);
for (int i = 1; i <s; i *= 2) {
copy(m, m + s, m + s);
m += s;
REP (j, s - i) m[j] = min(m[j], m[j + i]);
}
return ;
}
int query(int b, int e)
{
if (e < b) swap(e, b);
int k;
for (k = 0; (1 << (k + 1)) <= (e - b + 1); ++k) ;
return min(n[b + size * k], n[e + size * k - (1 << k) + 1]);
}
};
int main(int argc, char *argv[])
{
int tc;
cin >> tc;
while (tc--) {
int n;
cin >> n;
vector< pair<string, int> > v(n);
for (int i = 0; i < n; ++i) {
cin >> v[i].first;
v[i].second = i;
}
sort(v.begin(), v.end());
int h[n];
for (int i = 0; i + 1 < n; ++i) {
h[i] = 0;
for (int j = 0; j < min(v[i].first.size(), v[i+1].first.size()); ++j) {
if (v[i].first[j] != v[i + 1].first[j]) break;
++h[i];
}
}
RMQ rmq(h, n-1);
int ord[n];
for (int i = 0; i < n; ++i) {
ord[v[i].second] = i;
}
static int cnt = 0;
cout << "Case " << ++cnt << ":" << endl;
int q, a, b;
cin >> q;
while (q--) {
cin >> a >> b;
--a;
--b;
if (a == b) cout << v[ord[a]].first.size() << endl;
else {
if (ord[a] > ord[b]) swap(a, b);
cout << rmq.query(ord[a], ord[b] - 1) << endl;
}
}
}
return 0;
}
0 件のコメント :
コメントを投稿