题意:给出一个字符串,求出出现了恰好k次的子串的个数。

题解:恰好k次 = 至少k次 - 至少k+1次。答案转化为求至少出现k次的子串个数统计。构造好后缀数组以及很重要的Height数组之后。用一个k-1的窗子去滑动。窗子里边放着k-1个Height值(Height[ i+1 ],Height[ i+2 ],……,Height[ i+k ]),这样k-1个值就联系了k个后缀(SA[ i ],SA[ i+1 ],……,SA[ i+k ]),显然要求出这k-1个Height值的最小值,这个最小值就是这k个后缀的极大公共前缀了,假设是x,那么长度为1..x的前缀都在这个窗子里面出现了k次,也就是说他们都是符合答案的子串了。但是要考虑前边已经重复统计过了1..x中的一部分,考虑前一个窗子(Height[ i ],Height[ i+1 ],……,Height[ i+k-1 ]),讨论前面这个窗子的最小值y:1、y<x,那么说明上一个窗子的最小值一定是Height[ i ] ,因此上一个窗子统计了1..Height[ i ]部分,这一次需要统计Height[ i ]..x部分。2、y>x,那么就是说明前面窗子的极大公共前缀长度 比 这一个窗子的 极大公共前缀长,而这两个窗子有(i+1,i+2,,……,i+k-1)这k-2个元素是一样的。因此考虑所有的k个值(Height[ i ],Height[ i+1 ],……,Height[ i+k-1 ],Height[ i+k ]),1..x必然是他们的公共前缀,那么由于y>x,前面一个窗子已经统计完了1..y的部分,1..x的部分被计算在前一个窗子里面了,这一次不需要计算,为了和上边一个式子统一起来,我们考虑Height[ i ],显然有Height[ i ]>=y>x,进而有Height[ i ]>x。

因此结论是:用k-1的窗子从2开始滑动,维护窗子的最小值,然后每个窗子和刚刚移出窗子的那个Height作比较,如果min>Height,就统计min-Height。相反不统计。维护最小值用单调队列复杂度最低(priority_queue是nlogn,手写数组模拟是n)。构造后缀数组用倍增方法,复杂度是nlogn,因此整体复杂度是nlogn。

PS:CSDN上很火的一个五分钟学后缀数组的博客,代码是错误的,只需要把我下边这个AC的代码的统计答案部分移植到那个版本的代码中去,然后提交到HDU 6194就可以愉快的得到一个WA。假博客坑了我两整天的时间。但是我也没有找到那个代码具体错在哪里,感觉可能是他的基数排序比较丑,把自己弄挂了(第二关键字排序之后,肯定是n个位置都有一个序号,但如果在中间加入一个assert(p==n)就可以0ms得到WA,所以他的第二关键字排序有问题)。自己也是拿这道题入门的后缀数组,都怪自己太菜……

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MaxN=1e5+100;
const int MAXN = MaxN;
int cntA[MaxN],cntB[MaxN],tsa[MAXN],A[MAXN],B[MAXN];
int sa[MAXN],Rank[MAXN],h[MAXN];
char ch[MAXN];
struct Node{int val,index;Node(int val_,int index_):val(val_),index(index_){}bool operator < (const Node b)const{if (val==b.val){return b.index<index;}return b.val<val;}
};
priority_queue<Node>pq;
void GetSa(char *ch,int *sa,int *rank,int n){for(int i=0;i<MaxN;i++)  cntA[i]=0;for(int i=1;i<=n;i++)   cntA[ch[i]]++;for(int i=1;i<=MaxN;i++) cntA[i]+=cntA[i-1];for(int i=n;i;i--)  sa[cntA[ch[i]]--]=i;rank[sa[1]]=1;for(int i=2;i<=n;i++){rank[sa[i]]=rank[sa[i-1]];if(ch[sa[i]]!=ch[sa[i-1]])  rank[sa[i]]++;}for(int l=1;rank[sa[n]]<n;l<<=1){for(int i=0;i<MaxN;i++)  cntA[i]=0;for(int i=0;i<MaxN;i++)  cntB[i]=0;for(int i=1;i<=n;i++){cntA[A[i]=rank[i]]++;cntB[B[i]=(i+l<=n)?rank[i+l]:0]++;}for(int i=1;i<MaxN;i++)   cntB[i]+=cntB[i-1];for(int i=n;i;i--)  tsa[cntB[B[i]]--]=i;for(int i=1;i<MaxN;i++)  cntA[i]+=cntA[i-1];for(int i=n;i;i--)  sa[cntA[A[tsa[i]]]--]=tsa[i];rank[sa[1]]=1;for(int i=2;i<=n;i++){rank[sa[i]]=rank[sa[i-1]];if(A[sa[i]]!=A[sa[i-1]] || B[sa[i]]!=B[sa[i-1]])    rank[sa[i]]++;}}
}void GetHeight(char *ch,int *sa,int *rank,int *height,int n){GetSa(ch,sa,rank,n);for(int i=1,j=0;i<=n;i++){if(j)   j--;while(ch[i+j]==ch[sa[rank[i]-1]+j]) j++;height[rank[i]]=j;}
}
int GetK(int k,int n){int ans=0;k--;if(k==0){for(int i=1;i<=n;++i)   ans=ans+(n-sa[i]+1-h[i]);return ans;}while (!pq.empty())pq.pop();for (int i=2;i<=n;i++){while (!pq.empty()&&pq.top().index<i-k+1)pq.pop();pq.push(Node(h[i],i));if (i>k){int top = pq.top().val;int last = h[i-k];ans +=max(0,top-last);}}return ans;
}void Run(){int n,k;scanf("%d",&k);scanf("%s",ch+1);n=strlen(ch+1);GetHeight(ch,sa,Rank,h,n);printf("%d\n",GetK(k,n)-GetK(k+1,n));
}
int main(){int T;scanf("%d",&T);while(T--){Run();}return 0;
}

HDU 6194 string string string :后缀数组+单调队列 | 后缀自动机相关推荐

  1. POJ 3415 后缀数组+单调栈

    题目大意: 给定A,B两种字符串,问他们当中的长度大于k的公共子串的个数有多少个 这道题目本身理解不难,将两个字符串合并后求出它的后缀数组 然后利用后缀数组求解答案 这里一开始看题解说要用栈的思想,觉 ...

  2. POJ - 3415 Common Substrings(后缀数组+单调栈)

    题目链接:点击查看 题目大意:给出两个字符串,再给出一个k,问两个字符串中长度大于等于k的公共子串有多少个(种类可重复) 题目分析:因为涉及到了子串问题,先用后缀数组跑出height数组来,接下来如果 ...

  3. bzoj 3238: [Ahoi2013]差异(后缀数组+单调栈)

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 3443  Solved: 1562 [Submit][Stat ...

  4. [Ahoi2013]差异[后缀数组+单调栈]

    链接 解题思路:很明显前面∑1<=i<j<=nlen(Ti)+len(Tj)\sum_{1<=i<j<=n}len(T_i)+len(T_j)∑1<=i< ...

  5. [BZOJ 3238] [AHOI 2013] 差异 【后缀数组 + 单调栈】

    题目链接:BZOJ - 3238 题目分析 显然,这道题就是求任意两个后缀之间的LCP的和,这与后缀数组的联系十分明显. 求出后缀数组后,求出字典序相邻两个后缀的LCP,即 Height 数组. 那么 ...

  6. [bzoj3238]差异(后缀数组+单调栈)

    显然我们可以先把len(Ti)+len(Tj)的值先算出来,再把LCP减去.所有len(Ti)+len(Tj)的值为n*(n-1)*(n+1)/2,这个随便在纸上画一画就可以算出来的. 接下来问题就是 ...

  7. [BZOJ3238][AHOI2013]差异 [后缀数组+单调栈]

    题目地址 - GO-> 题目大意: 给定一个长度为 nn 的字符串SS,令TiTi表示它从第ii个字符开始的后缀,求以下这个式子的值: ∑1≤i<j≤nlen(Ti)+len(Tj)−2× ...

  8. 【BZOJ3879】SvT,后缀数组+单调栈维护sum

    Time:2016.08.15 Author:xiaoyimi 转载注明出处谢谢 如果有不明白的地方可以在下面评论问我 传送门 思路: 建立后缀数组求出Height 如果说对每次询问暴力求LCP累加的 ...

  9. 【HAOI2016/BZOJ4566】找相同字符 后缀数组+单调栈

    原题走这里 鉴于我实在不是很懂单调栈和单调队列这一系列东西,所以我决定稍微具体讲一下单调栈. 恩,本题实质上就是求两个字符串的公共子串数,其中只要出现位置不同,就算是不同的子串. 处理多个字符串的经典 ...

最新文章

  1. poj - 1651 Multiplication Puzzle
  2. VTK:动轴用法实战
  3. 约瑟夫环(丢手绢问题)
  4. 配送交付时间轻量级预估实践
  5. xtrabackup部分备份数据库 Partial Backups
  6. linux crypto cbc 接口,Linux 2.6.38.4: User-space interface for Crypto API
  7. 带你入门SpringCloud 之 通过SpringCloud Bus 自动更新配置
  8. vue从入门到开发--2-基本结构
  9. 洛谷P1238 走迷宫
  10. 64位和32位的区别
  11. linux centos7 配置ftp,Linux Centos7配置ftp服务器
  12. 想成为一名数据科学家?你得先读读这篇文章
  13. 重型柴油车OBD系统进入逻辑
  14. 今天学习腌菜,啦啦啦啦啦
  15. 配置本地parcels安装CDH 6.2.0
  16. 使用SQLyog导入EXCEL数据和合并数据表
  17. Bandwagon的配置记录(二) —— ftp文件传输
  18. 年会必备:18套年会快闪PPT
  19. Anaconda点开一直处于loading application状态
  20. netty 实现消息群发

热门文章

  1. 清空数据库所有表数据
  2. BackTrack5(BT5)硬盘安装
  3. websocket ws + wss 通信
  4. MDXMondrian介绍
  5. 程序员必备技能之英语学习(一)
  6. nvm安装使用及卸载
  7. 安装rhel-server-7.2系统
  8. Qt入门-文本框类QLineEdit和QTextEdit
  9. JavaScript ES6中的深拷贝和浅拷贝
  10. 计算机R3处理器,【新CPU】入门级真香!全新锐龙APU之R3-4200G/4350G评测