题目链接【http://acm.hdu.edu.cn/showproblem.php?pid=6194】

题意:

  给你一个长度不大于1e5的字符串,然后然你判断其子串严格出现k次的子串个数。

题解:

  后缀数组 + RMQ。首先说一下后缀数组里面的三个数组的作用:

  sa[i]    数组 : 排名为i的后缀是[sa[i] - end];
  Rank[i]     数组 : 后缀为[i - end]的排名是Rank[i]
  height[i]    数组 : 排名为i的后缀和排名为i - 1的后缀的最长前缀。

这里主要用到了height数组,首先我们考虑k == 1的情况,tmp = max(height[i], height[i + 1]),tmp表示排名为i的串和排名为i-1的串的公共前缀及排名为i+1的串和排名为i的串的公共前缀的最大值,那么可以知道排名为i的串中一共有len[i](排名为i的串的长度) - tmp个。

当k>1的时候,我们就要维护height的区间最小值了。从排名为k的串开始枚举(排名在k之前的串肯定出现不了k次),我们维护

int tmp = RMQMI(i - lenk + 2, i);
int x = max(height[i + 1], height[i - lenk + 1]);
if(tmp - x > 0) ans += tmp - x;

tmp,表示串i-lenk+1,到串i的公共不部分,表示公共部分的串出现了k次或者以上,再判断max(height[i + 1], height[i - lenk + 1]);,答案即为 tmp - x。

#include<bits/stdc++.h>
const int maxn = 1e6 + 5;
using namespace std;
char s[maxn];
int sa[maxn], t[maxn], t2[maxn], c[maxn], n;
int T, lenk;
void build_sa(int m)
{
int *x = t, *y = t2;
for(int i = 0; i < m; i++) c[i] = 0;
for(int i = 0; i < n; i++) c[x[i] = s[i]]++;
for(int i = 1; i < m; i++) c[i] += c[i - 1];
for(int i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;
for(int k = 1; k <= n; k <<= 1)
{
int p = 0;
for(int i = n - k; i < n; i++) y[p++] = i;
for(int i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i] - k;
for(int i = 0; i < m; i++) c[i] = 0;
for(int i = 0; i < n; i++) c[x[y[i]]]++;
for(int i = 0; i < m; i++) c[i] += c[i - 1];
for(int i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1;
x[sa[0]] = 0;
for(int i = 1; i < n; i++)
x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
if(p >= n) break;
m = p;
}
}
int cmp_suffix(char* pattern, int p, int m)
{
return strncmp(pattern, s + sa[p], m);
}
int Rank[maxn], height[maxn];
void getHeight()
{
int i, j, k = 0;
for(i = 0; i < n; i++) Rank[sa[i]] = i;
for(i = 0; i < n; i++)
{
if(k) k--;
j = sa[Rank[i] - 1];
while(s[i + k] == s[j + k]) k++;
height[Rank[i]] = k;
}
height[0] = 0;
}
int mi[maxn][20], lmt[maxn];
void InitRMQ()
{
int N = n - 1;
lmt[0] = -1;
for(int i = 1; i <= N; i++)
{
lmt[i] = ((i & (i - 1)) == 0) ? lmt[i - 1] + 1 : lmt[i - 1];
mi[i][0] = height[i];
}
for(int j = 1; j <= lmt[N]; j++)
{
for(int i = 1; i + (1 << j) - 1 <= N; i++)
mi[i][j] = min(mi[i][j - 1], mi[i + (1 << (j - 1))][j - 1]);
}
}
int RMQMI(int x, int y)
{
int k = lmt[y - x + 1];
return min(mi[x][k], mi[y - (1 << k) + 1][k]);
}
int main ()
{
scanf("%d", &T);
while(T--)
{
scanf("%d %s", &lenk, s);
n = strlen(s) + 1;
build_sa(127);
for(int i = 1; i <= n; i++) height[i] = 0;
getHeight();
int ans = 0;
if(lenk == 1)
{
for(int i = 1; i < n; i++)
{
int len = n - sa[i] - 1;
int tmp = max(height[i], height[i + 1]);
if(len - tmp > 0) ans += len - tmp;
}
}
else
{
InitRMQ();
for(int i = lenk; i < n; i++)
{
int tmp = RMQMI(i - lenk + 2, i);
int x = max(height[i + 1], height[i - lenk + 1]);
if(tmp - x > 0) ans += tmp - x;
}
}
printf("%d\n", ans);
}
return 0;
}

  

HDU 6194【后缀数组】的更多相关文章

  1. HDU 6194 后缀数组

    string string string Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Oth ...

  2. hdu 3948 后缀数组

    The Number of Palindromes Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 262144/262144 K (J ...

  3. HDU - 3948 后缀数组+Manacher

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3948 题意:给定一个字符串,求字符串本质不同的回文子串个数. 思路:主要参考该篇解题报告 先按照man ...

  4. HDU 5769 后缀数组

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5769 [2016多校contest-4] 题意:给定一个字符,还有一个字符串,问这个字符串存在多少个不 ...

  5. hdu 2459 (后缀数组+RMQ)

    题意:让你求一个串中连续重复次数最多的串(不重叠),如果重复的次数一样多的话就输出字典序小的那一串. 分析:有一道比这个简单一些的题spoj 687, 假设一个长度为l的子串重复出现两次,那么它必然会 ...

  6. hdu 3518(后缀数组)

    题意:容易理解... 分析:这是我做的后缀数组第一题,做这个题只需要知道后缀数组中height数组代表的是什么就差不多会做了,height[i]表示排名第i的后缀与排名第i-1的后缀的最长公共前缀,然 ...

  7. hdu 5442 (后缀数组)

    稍微学习了下第一次用后缀数组- - , 强行凑出答案 , 感觉现在最大的问题是很多算法都不知道 ,导致有的题一点头绪都没有(就像本题).  /*推荐 <后缀数组——处理字符串的有力工具>— ...

  8. HDU 5558 后缀数组

    思路: 这是一个错误的思路, 因为数据水才过= = 首先求出来后缀数组 把rank插到set里 每回差i两边离i近的rank值,更新 如果LCP相同,暴力左(右)继续更新sa的最小值 //By Sir ...

  9. HDU 4691 后缀数组+RMQ

    思路: 求一发后缀数组,求个LCP 就好了 注意数字有可能不只一位 (样例2) //By SiriusRen #include <bits/stdc++.h> using namespac ...

  10. K-th occurrence HDU - 6704 (后缀数组+二分线段树+主席树)

    大意: 给定串s, q个询问(l,r,k), 求子串s[l,r]的第kk次出现位置. 这是一篇很好的题解: https://blog.csdn.net/sdauguanweihong/article/ ...

随机推荐

  1. GridControl详解(六)样式设置

    表格样式:全局设置 例子: 例子: 列样式:只作用于当前的列 通用样式:外观设定 注意:样式设定都是相同的,Appearance前缀.

  2. 始终要重载toString

    本文涉及到的概念 1.重载toString方法的意义 2.两个注意事项   1.重载toString方法的意义 重载toString,返回关于当前实例的描述信息.这在调试错误,打印实例信息时,可以带来 ...

  3. 基于Node的Web聊天室

    1 项目名称 Web聊天室(<这是NodeJs实战>第二章的一个案例,把整个开发过程记录下来)

  4. linux编程之main()函数启动过程【转】

    转自:http://blog.csdn.net/gary_ygl/article/details/8506007 1 最简单的程序  1)编辑helloworld程序,$vim helloworld. ...

  5. Linux进程的创建函数fork()及其fork内核实现解析【转】

    转自:http://www.cnblogs.com/zengyiwen/p/5755193.html 进程的创建之fork() Linux系统下,进程可以调用fork函数来创建新的进程.调用进程为父进 ...

  6. HZ与Jiffies

    2.4 内核定时器 内核中许多部分的工作都高度依赖于时间信息.Linux内核利用硬件提供的不同的定时器以支持忙等待或睡眠等待等时间相关的服务.忙等待时,CPU 会不断运转.但是睡眠等待时,进程将放弃C ...

  7. Codeforces Round #434 (Div. 2)

    Codeforces Round #434 (Div. 2) 刚好时间对得上,就去打了一场cf,发现自己的代码正确度有待提高. A. k-rounding 题目描述:给定两个整数\(n, k\),求一 ...

  8. Linux中涉及到环境变量的文件

    1. 系统级 (a) /etc/profile : 在用户登录操作系统时,定制用户环境的第一个文件,应用于登录的每一个用户 ==> 该文件一般调用/etc/bash.bashrc文件 (b)/e ...

  9. Python学习笔记——迭代器和生成器

    1.手动遍历迭代器 使用next函数,并捕获StopIteration异常. def manual_iter(): with open('./test.py') as f: try: while Tr ...

  10. rcnn ->fast rcnn->faster rcnn物体检测论文

    faster rcnn中的rpn网络: 特征可以看做一个尺度51*39的256通道图像,对于该图像的每一个位置,考虑9个可能的候选窗口:三种面积{1282,2562,5122}×三种比例{1:1,1: ...