P2463 [SDOI2008]Sandy的卡片

知识点: SAM

原题面 Luogu


题意简述

给定 \(n\) 个字符串 \(S_1\sim S_n\)
定义两个子串相等,当且仅当它们长度相等,且一个串的全部元素加上一个数就会变成另一个串。
\(n\) 个串的最长公共子串。
\(1\le n\le 1000, 1\le |S_i|\le 1000\)


分析题意

一开始看错题啦!
区间加操作很烦人,考虑差分。
差分后第一个位置无用,将其去除,输出时答案 + 1。
变为多串最长公共子串问题,详见 SP1812 LCS2 - Longest Common Substring II


代码实现

//知识点:SAM
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <map>
#define ll long long
const int kMaxn = 3e5 + 10;
//=============================================================
int now_len[1010], S[1010][kMaxn];
int n, last = 1, node_num = 1, ans;
int cnt[kMaxn], id[kMaxn];
int len[kMaxn <<1], link[kMaxn << 1];
int minn[kMaxn << 1], maxx[kMaxn << 1];
std :: map <int, int> ch[kMaxn << 1];
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void GetMax(int &fir, int sec) {
  if (sec > fir) fir = sec;
}
void GetMin(int &fir, int sec) {
  if (sec < fir) fir = sec;
}
void Insert(int c_) {
  int p = last, now = last = ++ node_num;
  len[now] = len[p] + 1;
  for (; p && ! ch[p].count(c_); p = link[p]) ch[p][c_] = now;
  if (! p) {link[now] = 1; return ;} 
  int q = ch[p][c_];
  if (len[q] == len[p] + 1) {link[now] = q; return ;}
  int newq = ++ node_num;
  ch[newq] = ch[q];  
  link[newq] = link[q], len[newq] = len[p] + 1; 
  link[q] = link[now] = newq; 
  for (; p && ch[p][c_] == q; p = link[p]) ch[p][c_] = newq;
}
void TopSort() {
  for (int i = 1; i <= node_num; ++ i) cnt[len[i]] ++;
  for (int i = 1; i <= node_num; ++ i) cnt[i] += cnt[i - 1];
  for (int i = 1; i <= node_num; ++ i) id[cnt[len[i]] --] = i;
}
void Work(int *S_, int n_) {
  int now = 1, l = 0;
  for (int i = 2; i <= n_; ++ i) {
    int c = S_[i] - S_[i - 1];
    while (now && ! ch[now].count(c)) {
      now = link[now];
      l = len[now]; 
    }
    if (! now) {
      now = 1;
      l = 0;
      continue ;
    }
    ++ l;
    now = ch[now][c];
    GetMax(maxx[now], l);
  }
  for (int i = node_num; i; -- i) {
    int u = id[i], fa = link[u];
    GetMax(maxx[fa], std :: min(maxx[u], len[fa]));
    GetMin(minn[u], maxx[u]); 
    maxx[u] = 0; //注意清空,准备下一次匹配。 
  }
}
//=============================================================
int main() {
  int T = read();
  int min_len = 1e9 + 2077, min_pos;
  for (int i = 1; i <= T; ++ i) {
    now_len[i] = read();
    S[i][1] = read(); 
    for (int j = 2; j <= now_len[i]; ++ j) S[i][j] = read();
    if (now_len[i] < min_len) {
      min_len = now_len[i];
      min_pos = i;
    }
  }
  std :: swap(S[1], S[min_pos]);
  for (int i = 2; i <= min_len; ++ i) Insert(S[1][i] - S[1][i - 1]);
  TopSort();
  memset(minn, 63, sizeof (minn));
  for (int i = 2; i <= T; ++ i) Work(S[i], now_len[i]);
  for (int i = 1; i <= node_num; ++ i) GetMax(ans, minn[i]);
  printf("%d", ans + 1);
  return 0; 
}
posted @ 2020-08-20 11:21  Luckyblock  阅读(121)  评论(0编辑  收藏  举报