当前位置: 首页 > news >正文

【题解】洛谷 P4081 [USACO17DEC] Standing Out from the Herd P [后缀数组 SA]

 没学过指路:【详细注释 | 字符串算法集合 2】后缀自动机 SAM & 后缀数组 SA-CSDN博客


P4081 [USACO17DEC] Standing Out from the Herd P - 洛谷 (luogu.com.cn)

后缀数组 SA 做法

整出 height 数组后枚举字典序排名相邻的两个后缀,通过 LCP 统计答案。

至于判断哪个后缀是哪个字符串,看 sa 数组(后缀的起始位置)就行。

具体看我代码注释,太阴了这做法。

注意!!!如果你大数据输出为负数,请检查你的 s 数组是否为 int 类型。

最好是传入 s[i] - 'a',间隔符是否全部都不相同(防止不存在的公共子串)

代码:

#include<bits/stdc++.h>
using namespace std;typedef long long LL;
const int N = 4e5 + 10;char ss[N];
int n, m;
LL ed[N];
int c[N], x[N], y[N], sa[N];
int s[N];   // 这里一定要用 int !! void get_sa() {int i, j, k;memset(c, 0, sizeof(c));for (i = 1; i <= n; i ++) {c[x[i] = s[i]]  ++;}for (i = 1; i <= m; i ++) {c[i] += c[i - 1];}for (i = n; i >= 1; i --) {sa[c[x[i]]] = i;c[x[i]] --;}for (k = 1; k <= n; k <<= 1) {for (i = 1; i <= m; i ++) {c[i] = 0;}for (i = 1; i <= n; i ++) {y[i] = sa[i];}for (i = 1; i <= n; i ++) {c[x[y[i] + k]] ++;}for (i = 1; i <= m; i ++) {c[i] += c[i - 1];}for (i = n; i >= 1; i --) {sa[c[x[y[i] + k]]] = y[i];c[x[y[i] + k]] --;}for (i = 1; i <= m; i ++) {c[i] = 0;}for (i = 1; i <= n; i ++) {y[i] = sa[i];}for (i = 1; i <= n; i ++) {c[x[y[i]]] ++;}for (i = 1; i <= m; i ++) {c[i] += c[i - 1];}for (i = n; i >= 1; i --) {sa[c[x[y[i]]]] = y[i];c[x[y[i]]] --;}for (i = 1; i <= n; i ++) {y[i] = x[i];}for (m = 0, i = 1; i <= n; i ++) {if (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) {x[sa[i]] = m;}else {m ++;x[sa[i]] = m;}}if (m == n) {break;}}
}int height[N], rk[N];
int mp[N];
LL ans[N];void get_height() {int i, j, k;memset(height, 0, sizeof(height));for (i = 1; i <= n; i ++) {rk[sa[i]] = i;}for (k = 0, i = 1; i <= n; i ++) {if (rk[i] == 1) {continue;}if (k) {k --;}int j = sa[rk[i] - 1];while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) {k ++;}height[rk[i]] = k;}
}int main () {ios::sync_with_stdio(false);cin.tie(0);int T;cin >> T;n = 0; for (int i = 1; i <= T; i ++) {cin >> ss + 1;for (int j = 1; ss[j]; j ++) {n ++;s[n] = ss[j] - 'a';    // 这里一定要 - 'a'!!!不然会爆 long long mp[n] = i;}ed[i] = n;n ++;m = s[n] = 26 + i;   // 间隔符:别管是啥妖魔鬼怪不一样就行,同时更新上限 m // 只要字典序不插在中间干扰答案就行 }get_sa();get_height();LL mn = 0;// mn:从上一个不同字符串的字典序相邻后缀到当前后缀的 LCP 最小值 memset(ans, 0, sizeof(ans));for (int i = 1; i <= n; i ++) {LL L = height[i];mn = min(L, mn); int mx = mp[sa[i]], my = mp[sa[i - 1]];if (mx == my) {   // 相邻俩后缀属于一个字符串 ans[mx] += ed[mx] - sa[i] + 1 - L;// 字符串 mx 加上自己以 sa[i] 为开头的剩下的不重复子串// 比如说从 sa[i] 到字符串结尾还剩 abc,height 为 2// 那么从 a 开始有 3(字符串剩下长度)个后缀:a,ab,abc// 再减掉 2(a 和 ab),就是不重复子串 }else {ans[mx] += ed[mx] - sa[i] + 1 - L;// 和前面那个一样,都是 mx 自身的答案贡献 ans[my] += mn - L;/* *** 难点假设上一个不同字符串到当前的子串分别是:ba,bb,bbb,bbbb,bbbc除了中间三个全是 b 的所属同一个字符串 s,其他头尾两个都是不同的 mn = 1,字符串 s 的答案贡献在 i = bb 时减了 b 在 i = bbb 减了 b 和 bb 在 i = bbbb 时减了 b 和 bb 和 bbb在 i = bbbc 时减了 b 和 bb 和 bbb我们发现同一个 b 的贡献在和别的字符串匹配时减了 2 次但实际上 bbbb 字符串减 b 的贡献只用减 1 次(本来要减 4 次,但剩下的 b 在 mx = my 时把自身重复的都减掉了) (我们这里只讨论 bbbb 在减掉自身相重复的子串后,与外界相重复的子串应该怎么减)而 b 这个子串与其它两个字符串的后缀相重复,在 bbbb 内的贡献减 1 次就好所以还要将 mn 加回来 */mn = L;   // 更新 mn }}for (int i = 1; i <= T; i ++) {cout << ans[i] << "\n";}return 0;
}

http://www.dtcms.com/a/426936.html

相关文章:

  • 论测试驱动开发DD
  • QCustomPlot 性能优化与问题排查
  • 建网站要多少钱用自己的服务器手机网站 图标
  • 需求分析:对原始需求进行加工提炼
  • SFT/DPO/PPO/GRPO训练全解析
  • 海康 智能相机二开 绘制底图+测试工具应用框
  • DIY主机无网络安装PVE全记录:手机热点+笔记本网络共享实战
  • 从“氛围编程“到“氛围研究“:OpenAI的GPT-5与未来自动化研究之路
  • 手动编译 OpenCV 4.1.0 源码,生成 ARM64 动态库 (.so),然后在 Petalinux 中打包使用。
  • 乐从做网站wordpress lover主题
  • 品牌型网站制作有哪些公司自建房设计
  • 知微传感Dkam系列3D相机SDK例程篇:CSharp点云滤波
  • AEO 与 SEO 双引擎:整合策略赢得搜索全域可见性
  • 精读C++20设计模式:行为型设计模式:中介者模式
  • 【C++经典例题】逆波兰表达式求值:栈的经典应用与实现详解
  • mmcv 安装 2025
  • 设计模式(C++)详解——观察者模式(Observer)(2)
  • LeetCode 392 判断子序列
  • 树的存储结构
  • 2025年9月GESP(C++三级):数组清零
  • 怎样查看网站建设时间注册公司需要什么费用
  • Deepoc具身模型外拓板:重塑居家服务机器人的交互革命
  • cpuset v1
  • 2025年9月个人工作生活总结
  • Java SE “JDK1.8新特性”面试清单(含超通俗生活案例与深度理解)
  • 站台建筑资阳网站推广
  • 【论文阅读 | ECCV 2024 | DAMSDet:具有竞争性查询选择与自适应特征融合的动态自适应多光谱检测变换器】
  • 企业网站 三网系统好玩有趣的网站
  • 小程序的页面宽度 设置多少合适??
  • 基于libwebsockets与cJson的ASR Server实时语音识别实现指南