NOIP模拟赛20191007 T2 好文章【(卡)哈希 / 后缀自动机】
程序员文章站
2022-04-30 18:21:41
...
题目描述:
长为n的字符串,找出其中长度为m的不同子串的数量。
1<=m<=n<=200000
题目分析:
显然可以哈希。
但是出题人把我震惊了:
啪啪啪…(鼓掌)
于是我们知道了自然溢出很危险。。模数109左右更危险。。
所以我们哈希要用两个模数或者用一个超大模数加上大数乘法取模(x*y-(LL)((long double)x/p*y)*p)(模数比如说可以是5211600617818708273ll)
哈希的Code我就不放了,很好写。
我考场上写了后缀自动机来对拍,然而交了自然溢出的哈希上去又没能AK(无法AK的诅咒)
后缀自动机的做法就是从起点开始走长度为m的路径,路径条数就是不同的子串个数。但是不能直接走,那样可能退化成O(nm),我们观察自动机的每个节点,它到根虽然有多条路径,但是每条路径长度都是不同且连续的,所以只要它最长路径的长度>=m,它后缀连接指向的节点的最长路径<m,那么它就必然有一条通往起点的长为m的路径,所以可以O(1)判断,总复杂度就是O(n)的。
不过注意后缀自动机的空间是三倍。
Code:
#include<cstdio>
#include<cstring>
#define maxn 600005
int n,m,ans;
char s[maxn];
int last,sz,len[maxn],fail[maxn]={-1},ch[maxn][26];
void sa_extend(int c){
int cur=++sz,p=last,q; len[last=cur]=len[p]+1;
for(;p!=-1&&!ch[p][c];p=fail[p]) ch[p][c]=cur;
if(p==-1) fail[cur]=0;
else if(len[q=ch[p][c]]==len[p]+1) fail[cur]=q;
else{
int clone=++sz;
len[clone]=len[p]+1,memcpy(ch[clone],ch[q],sizeof ch[q]);
fail[clone]=fail[q],fail[q]=fail[cur]=clone;
for(;p!=-1&&ch[p][c]==q;p=fail[p]) ch[p][c]=clone;
}
}
int main()
{
scanf("%d%d%s",&n,&m,s);
for(int i=0;i<n;i++) sa_extend(s[i]-'a');
for(int i=1;i<=sz;i++) if(len[i]>=m&&len[fail[i]]<m) ans++;
printf("%d\n",ans);
}
上一篇: 剑指offer面试题58 - I. 翻转单词顺序(双指针)
下一篇: 后缀自动机概述