简略题意:问在一个字符串中出现次数等于k次的子串有多少种。

考虑按kk对height进行分组,则当前分组对答案的贡献为:
组内所有元素的的max(0, LCP - max(组内第一个元素与之前元素的LCP, 组内最后一个元素与之后元素的LCP))。
举例来看就是:
k=2k = 2
*1. abcabc
*2. abcabcabcabc
*3. abcabcabcabcabcabc
*4. abcabcabcabcabcabcabcabc
对于分组一(*1,*2), 其LCP为33,abc在这个组里作为前缀出现了22次,但由于*2和*3的LCP为66, 因此abcabc还会在*3内出现,abcabc出现的次数就超过了22次,当前分组的LCP对答案无贡献。
分组二同理。
而对于分组三(*3,*4),其LCP为99,说明abcabcabcabcabcabc在当前分组出现了22次,由于*2和*3的LCP为66,因此abcabcabcabc出现次数超过了22次,这部分对答案无贡献。因此分组三对答案的贡献为9−6=39-6=3,出现次数为22次的子串有abcabcaabcabca, abcabcababcabcab, abcabcabcabcabcabc。

需要特别处理的是k=1k=1的情况,不过想法和其他情况同理。

#include <bits/stdc++.h>using namespace std;typedef long long LL;
const LL N = 110000;LL n;
char str[N];namespace SA {LL sa[N], rank[N], height[N], s[N<<1], t[N<<1], p[N], cnt[N], cur[N];LL MIN[N][30];#define pushS(x) sa[cur[s[x]]--] = x#define pushL(x) sa[cur[s[x]]++] = x#define inducedSort(v) fill_n(sa, n, -1); fill_n(cnt, m, 0);                  \for (LL i = 0; i < n; i++) cnt[s[i]]++;                                  \for (LL i = 1; i < m; i++) cnt[i] += cnt[i-1];                           \for (LL i = 0; i < m; i++) cur[i] = cnt[i]-1;                            \for (LL i = n1-1; ~i; i--) pushS(v[i]);                                  \for (LL i = 1; i < m; i++) cur[i] = cnt[i-1];                            \for (LL i = 0; i < n; i++) if (sa[i] > 0 &&  t[sa[i]-1]) pushL(sa[i]-1); \for (LL i = 0; i < m; i++) cur[i] = cnt[i]-1;                            \for (LL i = n-1;  ~i; i--) if (sa[i] > 0 && !t[sa[i]-1]) pushS(sa[i]-1)void sais(LL n, LL m, LL *s, LL *t, LL *p) {LL n1 = t[n-1] = 0, ch = rank[0] = -1, *s1 = s+n;for (LL i = n-2; ~i; i--) t[i] = s[i] == s[i+1] ? t[i+1] : s[i] > s[i+1];for (LL i = 1; i < n; i++) rank[i] = t[i-1] && !t[i] ? (p[n1] = i, n1++) : -1;inducedSort(p);for (LL i = 0, x, y; i < n; i++) if (~(x = rank[sa[i]])) {if (ch < 1 || p[x+1] - p[x] != p[y+1] - p[y]) ch++;else for (LL j = p[x], k = p[y]; j <= p[x+1]; j++, k++)if ((s[j]<<1|t[j]) != (s[k]<<1|t[k])) {ch++; break;}s1[y = x] = ch;}if (ch+1 < n1) sais(n1, ch+1, s1, t+n, p+n1);else for (LL i = 0; i < n1; i++) sa[s1[i]] = i;for (LL i = 0; i < n1; i++) s1[i] = p[sa[i]];inducedSort(s1);}template<typename T>LL mapCharToLL(LL n, const T *str) {LL m = *max_element(str, str+n);fill_n(rank, m+1, 0);for (LL i = 0; i < n; i++) rank[str[i]] = 1;for (LL i = 0; i < m; i++) rank[i+1] += rank[i];for (LL i = 0; i < n; i++) s[i] = rank[str[i]] - 1;return rank[m];}template<typename T>void suffixArray(LL n, const T *str) {LL m = mapCharToLL(++n, str);sais(n, m, s, t, p);for (LL i = 0; i < n; i++) rank[sa[i]] = i;for (LL i = 0, h = height[0] = 0; i < n-1; i++) {LL j = sa[rank[i]-1];while (i+h < n && j+h < n && s[i+h] == s[j+h]) h++;if (height[rank[i]] = h) h--;}}void RMQ_init(){for(LL i=0; i<n; i++) MIN[i][0] = height[i+1];for(LL j=1; (1<<j)<=n; j++){for(LL i=0; i+(1<<j)<=n; i++){MIN[i][j] = min(MIN[i][j-1], MIN[i+(1<<(j-1))][j-1]);}}}LL RMQ(LL L, LL R){LL k = 0;while((1<<(k+1)) <= R-L+1) k++;return min(MIN[L][k], MIN[R-(1<<k)+1][k]);}LL LCP(LL i, LL j){return RMQ(i, j-1);}void init(char *str){str[n] = 0;suffixArray(n, str);RMQ_init();}void solve(LL k) {LL ans = 0;height[n+1] = 0;if(k == 1) {for(LL i = 1; i <= n; i++)ans += max(0LL, n - sa[i] - max(height[i], height[i+1]));} else {for(LL i = 1; i + k - 1 <= n; i ++) {ans += max(0LL, LCP(i, i+k-1) - max(height[i], height[i+k]));}}printf("%lld\n", ans);}
};LL t;
LL k;int main() {scanf("%lld", &t);while(t--) {scanf("%lld", &k);scanf("%s", str);n = strlen(str);SA::init(str);SA::solve(k);}return 0;
}

HDU6194 后缀数组相关推荐

  1. HDU6194(后缀数组)

    题意:告诉你一个字符串和k , 求这个字符串中有多少不同的子串恰好出现了k 次. 解题思路:先用后缀数组进行算出height,然后用ST表维护,然后用区间长为k进行查询,找出最小的height,目的是 ...

  2. HDU6194 后缀数组的应用

    题目大意: 给你一个串,让你统计有多少个子串出现了恰好k次,可重复的子串. 分析: 第一道后缀数组的题目. 我们先求出sa数组和,height数组,然后我们不难发现,我们需要枚举区间k,在[i,i+k ...

  3. HDU 6194 string string string 后缀数组 + RMQ(线段树)

    传送门:HDU6194 题意:问给定字符串中有多少种出现k次的子串. 思路:首先想到后缀数组经典问题,求出现不少于k次的子串的最大长度,类似的这题肯定就是在height数组上搞事情啦. 将height ...

  4. 寻找一个字符串的重复子串 后缀数组

    什么是后缀数组 令字符串 S=S[1]S[2]...S[n]S=S[1]S[2]...S[n]{\displaystyle S=S[1]S[2]...S[n]} , S[i,j]S[i,j]{\dis ...

  5. 【2012百度之星/资格赛】H:用户请求中的品牌 [后缀数组]

    时间限制: 1000ms 内存限制: 65536kB 描述 馅饼同学是一个在百度工作,做用户请求(query)分析的同学,他在用户请求中经常会遇到一些很奇葩的词汇.在比方说"johnsonj ...

  6. Boring counting HDU - 3518 (后缀数组)

    Boring counting \[ Time Limit: 1000 ms \quad Memory Limit: 32768 kB \] 题意 给出一个字符串,求出其中出现两次及以上的子串个数,要 ...

  7. HDU4080 Stammering Aliens(二分 + 后缀数组)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=4080 Description Dr. Ellie Arroway has establish ...

  8. 后缀数组 + Hash + 二分 or Hash + 二分 + 双指针 求 LCP ---- 2017icpc 青岛 J Suffix (假题!!)

    题目链接 题目大意: 就是给你n个串每个串取一个后缀,要求把串拼起来要求字典序最小!! sum_length_of_n≤5e5sum\_length\_of\_n\leq 5e5sum_length_ ...

  9. 后缀数组 ---- 2018~2019icpc焦作H题[后缀数组+st表+二分+单调栈]

    题目链接 题目大意: 给出nnn个数,定义f[l,r]f[l,r]f[l,r]表示 区间[l,r][l,r][l,r]的最大值,求所有 子区间的最大值的和,要求相同的子区间只能算一次 比如数列 5 6 ...

最新文章

  1. Oracle 11g-oracle 启动与关闭
  2. pytorch 安装 pip+windows10+python3.6+CUDA10.0
  3. 梳理:python—同一个类中的方法调用
  4. 十分钟了解分布式计算:GraphLab
  5. 从来没有一种技术是为了解决复用、灵活组合、定制开发的问题
  6. 前端学习(1271):async/await处理多个异步请求
  7. java猜数游戏有次数限制_“去小学化”之后,可以这样开展数学游戏,让孩子玩中学,学中乐!...
  8. 从今天开始学习iOS开发(iOS 7版)-- 概论 (一)
  9. 二维树状数组 探索进行中
  10. 拓端tecdat|R语言使用马尔可夫链Markov Chain, MC来模拟抵押违约
  11. MySQL数据库基础
  12. mysql 数字正则_mysql 正则
  13. 2008服务器系统初始密码,2008服务器地址和密码
  14. 有理数加法C语言pta,有理数
  15. 解决Mac系统更新后,SVN无法使用的问题
  16. 概率论复习总结——基本概念
  17. Arduino Nano 引脚分配图及定义详解
  18. QQ登录显示无法访问个人文件夹解决办法
  19. ResNeSt: Split-Attention Networks(ResNet改进版本)
  20. 2017年朋友圈最爆笑新闻,哈哈哈哈哈哈哈哈哈

热门文章

  1. 计算机网络期末复习提纲,湖南科技大学《计算机网络微课堂》课题笔记
  2. python docx文本替换保留样式
  3. tcp 与udp 的区别
  4. 用python制作网盘_3分钟教你用python制作一个简单词云
  5. ARMv7和ARMv8架构比较
  6. 为什么要用Handler,怎么用Handler
  7. 导航栏: UINavigationBar用法
  8. 机器学习面试题之机器学习基础(一)
  9. linux操作系统安全防护
  10. 如何彻底卸载java环境