【ybt金牌导航2-2-3】【POJ 3693】连续重复子串 / Maximum repetition substring

连续重复子串 / Maximum repetition substring

题目链接:ybt金牌导航2-2-3 / POJ 3693

题目大意

给你一个字符串,要你找一个子串,让它可以由另一个子串复制 x 次得到,而且这个 x 是最大的那个。
要你输出这个子串。

思路

首先你看到复制多次得到,想到可以用 LCP 来求。
什么意思呢?画个图就清楚了。
在这里插入图片描述
像这样等量代换一下,就可以说明它是复制多次得到了。

那你可以看出,你可以枚举两个的左边 x,y,然后得到的 LCPz,那个数就是 (yx)/z+1

LCP 要求两两之间容易想到用 SA,那么接着的问题就是枚举 x,y 也会超时。

那我们考虑怎么搞。
发现枚举两左边不知道怎么搞,我们考虑先枚举两个左边相差的长度(也就是要复制的字符串一份的长度),然后再枚举一个左边的。
然后似乎还是没有什么想法对吧。
那我们想到,你确定了一个的长度 L,那就说明,你最终根据它选到的子串一定会包含一些位置的其中一个。没错,就是 s0,sL,s2L,...
那你就可以枚举这些点,然后从它们开始找。

然后这时候你会发现中间的也可以做左边,那你求完长度之后,你再往左也延伸。
(或者说看从更左的地方会不会更优)

然后最后记录下最多循环次数的长度,然后根据长度求字典序最小的就好了。
求字典序最小就直接按后缀数组枚举,然后枚举记录的长度看是不是符合,符合就输出。

代码

#include<cmath> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; char s[300001]; int n, xx[300001], yy[300001], tong[300001]; int fir[300001], kind, ynum, height[300001][22]; int rank[300001], size[500001], max_time; int final_st, final_L, tmp, sa[300001], r[300001]; void sort_(int m, int *x, int *y) {//基数排序 for (int i = 1; i <= m; i++) tong[i] = 0; for (int i = 1; i <= n; i++) tong[x[i]]++; for (int i = 2; i <= m; i++) tong[i] += tong[i - 1]; for (int i = n; i >= 1; i--) sa[tong[fir[i]]--] = y[i]; } void SA(int *r, int *sa, int n, int m) {//倍增求SA数组 int *x = xx, *y = yy; for (int i = 1; i <= n; i++) x[i] = r[i]; for (int i = 1; i <= n; i++) y[i] = i; for (int i = 1; i <= n; i++) fir[i] = x[y[i]]; sort_(m, x, y); for (int j = 1; j < n; j <<= 1) { ynum = 0; for (int i = n - j + 1; i <= n; i++) y[++ynum] = i; for (int i = 1; i <= n; i++) if (sa[i] > j) y[++ynum] = sa[i] - j; for (int i = 1; i <= n; i++) fir[i] = x[y[i]]; sort_(m, x, y); swap(x, y); kind = 1; x[sa[1]] = 1; for (int i = 2; i <= n; i++) if (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + j] == y[sa[i - 1] + j]) x[sa[i]] = kind; else x[sa[i]] = ++kind; if (kind == n) break; m = kind; } } void get_height(int *r, int *sa, int n) {//求height数组 int k = 0, j; for (int i = 1; i <= n; i++) rank[sa[i]] = i; for (int i = 1; i <= n; i++) { if (k) k--; j = sa[rank[i] - 1]; while (r[i + k] == r[j + k] && i + k <= n && j + k <= n) k++; height[rank[i]][0] = k; } } int query(int x, int y) {//ST表查询LCP if (x > y) swap(x, y); x++; int half = 0; while (x + (1 << (half + 1)) <= y) half++; return min(height[x][half], height[y - (1 << half) + 1][half]); } int main() { scanf("%s", s + 1); n = strlen(s + 1); while (n != 1 || s[1] != '#') { max_time = 0; size[0] = 0; for (int i = 1; i <= n; i++) r[i] = s[i]; SA(r, sa, n, 300); get_height(r, sa, n); for (int i = 1; i <= 20; i++)//ST表预处理LCP for (int j = 1; j <= n && j + (1 << i) - 1 <= n; j++) height[j][i] = min(height[j][i - 1], height[j + (1 << (i - 1))][i - 1]); for (int L = 1; L <= n; L++) {//枚举长度 for (int j = 1; j + L <= n; j += L) {//枚举必会碰的位置 int LCP_ = query(rank[j], rank[j + L]); int re = LCP_ / L + 1; int bef = j - (L - (LCP_ % L));//可能前面一段也有 if (bef >= 0 && LCP_ % L != 0 && query(rank[bef], rank[bef + L]) >= j - bef) re++; if (re > max_time) {//取最优 max_time = re; size[0] = 0; size[++size[0]] = L; } else if (re == max_time) { size[++size[0]] = L; } } } final_st = 0;//找字典序最小的 for (int i = 1; i <= n; i++) {//记得这里枚举的不是字符串下标,而是后缀排名 for (int j = 1; j <= size[0]; j++) if (query(rank[sa[i]], rank[sa[i] + size[j]]) >= (max_time - 1) * size[j]) { final_st = sa[i]; final_L = size[j]; break; } if (final_st) break; } printf("Case %d: ", ++tmp); for (int i = final_st; i <= final_st + max_time * final_L - 1; i++) printf("%c", s[i]); printf("\n"); scanf("%s", s + 1); n = strlen(s + 1); } return 0; }

__EOF__

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