您的位置:首页 > 其它

POJ 3693 Maximum repetition substring

2012-03-10 20:05 363 查看
题目大意:

一个字符串的重复数x的含义是, 它可以由至多x个相同的字符串连接而成. 现在给出一个长度为N的字符串, N不超过100000, 求出它的重复数最大且字典序最小的子串.

简要分析:

论文题, 思路很有意思. 我们定义重复的字符串为循环节, 那么我们就枚举循环节长度L, 那么如果答案存在, 则必定包含s[0], s[L], s[2 * L]等中相邻的连个. 我们再枚举j, 则相当于我们假定s[j * L]和s[(j + 1) * L]在答案中, 且s[j * L]在第一个循环节内. 这里我们需要求后缀j * L和后缀(j + 1) * L的最长公共前缀, 这个可以用后缀数组和ST算法做好预处理后O(1)回答. 设LCP长度为M, 则答案显然为M / L + 1, 但这不一定是最好的, 因为答案的首尾不一定再我们枚举的位置上. 我的解决方法是, 我们考虑M % L的值的意义, 我们可以认为是后面多了M % L个字符, 但是我们更可以想成前面少了(L - M % L)个字符! 所以我们求后缀j * L - (L - M % L)与后缀(j + 1) * L - (L - M % L)的最长公共前缀, 看是否不小于L. 对于字典序的保证, 我的算法没有保证复杂度, 因为我是从每个j * L位置暴力往前for答案的初始位置, 答案的比较用后缀数组就好, 中间加上各种剪枝跑得还凑合. 在忽略那个暴力的情况下, 算法复杂度为O(N * (1 / 1 + 1 / 2 + ... + 1 / N)) = O(NlogN).

代码实现:

View Code

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAX_N = 100000;
char s[MAX_N + 2];
int cas, n, sa[MAX_N + 1], rank[MAX_N + 1], cnt[MAX_N + 1], height[MAX_N + 1];
int dp[17][MAX_N + 1], log2[MAX_N + 1];

struct node_t {
int v[2], p;
bool operator == (const node_t &t) const {
return v[0] == t.v[0] && v[1] == t.v[1];
}
} nd[MAX_N + 1], tp[MAX_N + 1];

struct ans_t {
int rep, start, len;
bool operator < (const ans_t &t) const {
if (rep != t.rep) return rep < t.rep;
if (rank[start] != rank[t.start]) return rank[start] > rank[t.start];
return len > t.len;
}
} ans, tmp;

void ra(int b) {
for (int i = 1; i >= 0; i --) {
memset(cnt, 0, sizeof(int) * (b + 1));
for (int j = 1; j <= n; j ++) cnt[nd[j].v[i]] ++;
for (int j = 1; j <= b; j ++) cnt[j] += cnt[j - 1];
for (int j = n; j >= 1; j --) tp[cnt[nd[j].v[i]] --] = nd[j];
memcpy(nd, tp, sizeof(node_t) * (n + 1));
}
for (int i = 1, j = 1, k = 1; i <= n; i = j, k ++)
while (j <= n && nd[j] == nd[i]) rank[nd[j ++].p] = k;
}

void rmq_init() {
memset(dp, 0x3f, sizeof(dp));
for (int i = 1; i <= n; i ++) dp[0][i] = height[i];
for (int i = 1; (1 << i) < n; i ++) {
for (int j = 1; j <= n; j ++) {
dp[i][j] = dp[i - 1][j];
if (j + (1 << (i - 1)) <= n) dp[i][j] = min(dp[i][j], dp[i - 1][j + (1 << (i - 1))]);
}
}
}

int rmq_ask(int l, int r) {
if (l > r) swap(l, r);
int st = log2[r - l + 1];
return min(dp[st][l], dp[st][r - (1 << st) + 1]);
}

int query(int l, int r) {
l = rank[l], r = rank[r];
if (l > r) swap(l, r);
return rmq_ask(++ l, r);
}

void solve() {
n = strlen(s + 1);
for (int i = 1; i <= n; i ++) {
nd[i].v[0] = s[i];
nd[i].v[1] = 0;
nd[i].p = i;
}
ra(255);
for (int j = 1; j < n; j <<= 1) {
for (int i = 1; i <= n; i ++) {
nd[i].v[0] = rank[i];
nd[i].v[1] = i + j <= n ? rank[i + j] : 0;
nd[i].p = i;
}
ra(n);
}
for (int i = 1; i <= n; i ++) sa[rank[i]] = i;
for (int i = 1, j, k = 0; i <= n; height[rank[i ++]] = k)
for (k ? k -- : 0, j = sa[rank[i] - 1]; s[i + k] == s[j + k]; k ++);
rmq_init();

ans.rep = 1;
ans.start = 1;
ans.len = 1;
for (int i = 2; i <= n; i ++)
if (s[i] < s[ans.start]) ans.start = i;

for (int i = 1; i < n; i ++)
for (int j = 1; j + i <= n; j += i) {
int t = query(j, j + i);
if (t < i) continue;
int k = j - (i - t % i);
t = t / i + 1;
if (t + 1 < ans.rep) continue;
if (t + 1 == ans.rep && (k < 1 || query(k, k + i) < i)) continue;

int p = (t + 1 == ans.rep ? k : j);
for ( ; p != j - i && p >= 1; p --) {
t = query(p, p + i);
t = t / i + 1;
if (t < ans.rep) break;
tmp.rep = t;
tmp.start = p;
tmp.len = i;
if (ans < tmp) ans = tmp;
}
}

for (int i = 0; i < ans.len * ans.rep; i ++) printf("%c", s[ans.start + i]);
printf("\n");
}

int main() {
log2[1] = 0;
for (int i = 2; i <= MAX_N; i ++) {
if ((i & (i - 1)) == 0) log2[i] = log2[i - 1] + 1;
else log2[i] = log2[i - 1];
}
while (scanf("%s", s + 1) && s[1] != '#') {
printf("Case %d: ", ++ cas);
solve();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: