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;
}
作者@Luckyblock,转载请声明出处。