SPOJ REPEATS - Repeats - 后缀数组

Description

求一个字符串内重复次数最多的连续字串。

Solution

丢一个hihocoder的链接

先考虑如何如何求一个串给定的串的最大重复次数,枚举一个可能的循环节长度 \(l\),然后求原串和原串去掉前 \(l\) 个字符后两个串的 \(lcp\)(最长公共前缀),如果能完全匹配上,就是一个循环节,且循环次数为 \(lcp(l,i)/l + 1\)

所以我们可以先枚举一个可能的长度 \(l\),然后再枚举起点 \(i\),然后通过求 \(suf[i],suf[i+l]\)\(lcp\) 进行判断。但是其实并不需要枚举每个 \(i\),只需要枚举 \(l\) 的整数倍。

如果最优串的开始位置恰好在 \(l\) 的倍数上,那我们找到的最大的 \(k\) 就是正确答案。

如果不在 \(l\) 的倍数上,那么只可能是在 \(i-(l-lcp(i,i+l)\% l)\)。(具体证明戳上面的链接)

Code

#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;

const int _ = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int N, rk[_], sa[_], height[_];
char s[_];

void SA() {
  int M = 125, p;
  static int buc[_], id[_], fir[_], oldrk[_];
  fill(buc + 1, buc + M + 1, 0);
  for (int i = 1; i <= N; ++i) ++buc[rk[i] = s[i]];
  for (int i = 1; i <= M; ++i) buc[i] += buc[i - 1];
  for (int i = N; i >= 1; --i) sa[buc[rk[i]]--] = i;
  for (int len = 1; len < N; len <<= 1, M = p) {
    p = 0;
    for (int i = N; i > N - len; --i) id[++p] = i;
    for (int i = 1; i <= N; ++i)
      if (sa[i] > len) id[++p] = sa[i] - len;
    fill(buc + 1, buc + M + 1, 0);
    for (int i = 1; i <= N; ++i) ++buc[fir[i] = rk[id[i]]];
    for (int i = 1; i <= M; ++i) buc[i] += buc[i - 1];
    for (int i = N; i >= 1; --i) sa[buc[fir[i]]--] = id[i];
    copy(rk + 1, rk + N + 1, oldrk + 1);
    p = 0;
    for (int i = 1; i <= N; ++i) {
      if (i == 1)
        rk[sa[i]] = ++p;
      else {
        int x = sa[i], y = sa[i - 1];
        if (oldrk[x] == oldrk[y] && oldrk[x + len] == oldrk[y + len])
          rk[sa[i]] = p;
        else
          rk[sa[i]] = ++p;
      }
    }
    if (p == N) break;
  }
  for (int i = 1, k = 0; i <= N; ++i) {
    if (rk[i] == 1)
      k = 0;
    else {
      if (k > 0) --k;
      int j = sa[rk[i] - 1];
      while (i + k <= N && j + k <= N && s[i + k] == s[j + k]) ++k;
    }
    height[rk[i]] = k;
  }
}

namespace ST {
int st[_][21], lg[_];
void init() {
  lg[0] = -1;
  for (int i = 1; i <= N; ++i) st[i][0] = height[i], lg[i] = lg[i >> 1] + 1;
  for (int j = 1; j <= 20; ++j)
    for (int i = 1; i + (1 << j) - 1 <= N; ++i)
      st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}
int query(int x, int y) {
  if (x > y) swap(x, y);
  ++x;
  int s = lg[y - x + 1];
  return min(st[x][s], st[y - (1 << s) + 1][s]);
}
}  // namespace ST

int lcp(int l, int r) {
  int x = rk[l], y = rk[r];
  return ST::query(x, y);
}

int ans = 0;
void solve() {
  for (int L = 1; L <= N; L++) {
    for (int i = 1; i + L <= N; i += L) {
      int R = lcp(i, i + L);
      ans = max(ans, R / L + 1);
      if (i >= L - R % L) {
        ans = max(lcp(i - L + R % L, i + R % L) / L + 1, ans);
      }
    }
  }
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("repeat.in", "r", stdin);
  freopen("repeat.out", "w", stdout);
#endif
  int T;
  scanf("%d", &T);
  while (T--) {
    ans = 0;
    scanf("%d", &N);
    for (int i = 1; i <= N; ++i) {
      char tmp[5];
      scanf("%s", tmp);
      s[i] = tmp[0];
    }
    SA();
    ST::init();
    solve();
    printf("%d\n", ans);
  }
  return 0;
}
posted @ 2020-01-08 14:05  newbielyx  阅读(153)  评论(0编辑  收藏  举报