要找一个字符串,使得它的长度尽可能的大,而且对于每个字符串,它在字符串或者它的翻转串的其中一个串中出现过。
只要输出最长的长度即可。
最长公共子串
题目大意
要找一个字符串,使得它的长度尽可能的大,而且对于每个字符串,它在字符串或者它的翻转串的其中一个串中出现过。
只要输出最长的长度即可。
思路
这道题我们考虑用 SA 加二分来做,虽然有很多解法。
首先,你把每个字符串和他们的翻转串拼接到一个字符串中,然后中间用互不相同的字符隔开。
然后跑 SA,得出 sai。
然后想到最长公共子串,我们会想到求 height 数组。
那你如果要二分答案,你就要找到一个区间,使得它包含了每个字符串的,然后它 height 值里面的最小值还要大于你二分的答案。
那这个你扫一遍过去找到每个区间来看行不行就可以了。
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n, m, T, t;
int p, tmp, sn, c[100001], height[100001], str[100001];
int sa[100001], rak[100001], tp[100001], tax[100001];
char s[100001];
queue <int> q;
bool in[100001];
void csh() {
n = 0;
tmp = 0;
memset(sa, 0, sizeof(sa));
memset(rak, 0, sizeof(rak));
memset(tp, 0, sizeof(tp));
memset(tax, 0, sizeof(tax));
memset(c, 0, sizeof(c));
memset(s, 0, sizeof(s));
memset(height, 0, sizeof(height));
memset(str, 0, sizeof(str));
memset(in, 0, sizeof(in));
}
void paixu() {
for (int i = 1; i <= m; i++)
tax[i] = 0;
for (int i = 1; i <= n; i++)
tax[rak[i]]++;
for (int i = 2; i <= m; i++)
tax[i] += tax[i - 1];
for (int i = n; i >= 1; i--)
sa[tax[rak[tp[i]]]--] = tp[i];
}
void SA() {
for (int i = 1; i <= n; i++) {
rak[i] = c[i];
tp[i] = i;
}
m = 1000;
paixu();
for (int w = 1; w < n; w <<= 1) {
m = p;
p = 0;
for (int i = n - w + 1; i <= n; i++)
tp[++p] = i;
for (int i = 1; i <= n; i++)
if (sa[i] > w) tp[++p] = sa[i] - w;
paixu();
swap(tp, rak);
p = 1;
rak[sa[1]] = 1;
for (int i = 2; i <= n; i++)
if (tp[sa[i]] == tp[sa[i - 1]] && tp[sa[i] + w] == tp[sa[i - 1] + w])
rak[sa[i]] = p;
else rak[sa[i]] = ++p;
if (p == n) break;
}
}
void get_height() {
int now = 0;
for (int i = 1; i <= n; i++) rak[sa[i]] = i;
for (int i = 1; i <= n; i++) {
if (now) now--;
for (int j = sa[rak[i] - 1]; c[i + now] == c[j + now]; now++);
height[rak[i]] = now;
}
}
bool check(int mid) {
while (!q.empty()) q.pop();
memset(in, 0, sizeof(in));
for (int i = 2; i <= n; i++)
if (height[i] >= mid) {
if (!in[str[sa[i]]]) {
q.push(str[sa[i]]);
in[str[sa[i]]] = 1;
}
if (!in[str[sa[i - 1]]]) {
q.push(str[sa[i - 1]]);
in[str[sa[i - 1]]] = 1;
}
}
else {
if (q.size() == t) return 1;
while (!q.empty()) q.pop();
memset(in, 0, sizeof(in));
}
if (q.size() == t) return 1;
return 0;
}
void work() {
if (t == 1) {
printf("%d\n", sn);
return ;
}
int lef = 0, rig = 100, re;
while (lef <= rig) {
int mid = (lef + rig) >> 1;
if (check(mid)) {
re = mid;
lef = mid + 1;
}
else rig = mid - 1;
}
printf("%d\n", re);
}
int main() {
scanf("%d", &T);
for (int times = 1; times <= T; times++) {
csh();
scanf("%d", &t);
for (int ii = 1; ii <= t; ii++) {
scanf("%s", s + 1);
sn = strlen(s + 1);
for (int i = 1; i <= sn; i++) {
c[++n] = s[i] - 'A' + 2 * t + 1;
str[n] = ii;
}
c[++n] = ++tmp;
str[n] = ii;
for (int i = sn; i >= 1; i--) {
c[++n] = s[i] - 'A' + 2 * t + 1;
str[n] = ii;
}
c[++n] = ++tmp;
}
SA();
get_height();
work();
}
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现