【ybt金牌导航2-2-1】最长公共子串

最长公共子串

题目链接:ybt金牌导航2-2-1

题目大意

要找一个字符串,使得它的长度尽可能的大,而且对于每个字符串,它在字符串或者它的翻转串的其中一个串中出现过。
只要输出最长的长度即可。

思路

这道题我们考虑用 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;//中间用互不相同的字符隔开str[n] = ii; } SA();//跑 SA get_height();//得出 height 数组 work();//二分答案 } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_2-2-1.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(34)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示