AC自动机好厉害啊www所以我要学后缀自动机和后缀数组啦(有什么关系吗魂淡(╯‵□′)╯︵┻━┻)
没关系这并不妨碍什么= =
——————————————–线割分是我>w<————————————————–
根据方法不同,字符串匹配算法/数据结构分成了前缀和后缀两大类.前缀以AC自动机,KMP和Trie最为出名,后缀有代表性的就是后缀自动机/后缀数组/后缀树.
我就是学后缀你来咬我啊www
先来几个定义~(≧▽≦)/~
一.通常我们用
二.字符串是由
三.字符串s中第i位到第j位的连续字符构成的字符串,称为s的一个子串.注意,空串也算s的一个子串.
四.选取s的第i位,则从第一位到第i位的子串称为s的一个前缀,相应的第i位到第n位构成的子串称为s的一个后缀.
五.字典序的比较原则,是从两个串s1,s2的第一位开始,逐个向后比较,若存在一个字符
显然对一个字符串的每一个后缀,他们的字典序都不可能相等,这也是使用后缀数据结构的重要条件.
基础知识说完了,该开始说后缀数组啦=- =
后缀数组:我们先把字符串的所有后缀按照字典序从小到大排序,然后建立一个叫sa的数组,sa[i]表示排序后第i个字符串表示的后缀是从原串s的第几位开始的.
我们用Suffix[i]表示第i位起的后缀,则很显然
后缀数组从不会独自出现,和它同时使用的是名次数组.
名次数组:定义名次数组rank,则rank[i]表示原串中第i位开始记的后缀在所有后缀里字典序从小到大排多少
故后缀数组sa和rank其实是一对互逆运算.
怎么互逆?有SA[i]=j, 则 Rank[j]=i
因此,这两个数组我们只要求出了任意一个,就可以
那么问题来了.怎样构造后缀数组?
显然最裸的思路是搞出所有后缀然后让他们排序再搞去吧= =
但是显然不行QAQ.
比较两个字符串是
这么慢的话要你何用(╯‵□′)╯︵┻━┻
于是人们提出了两种方法:DA(倍增)和DC3算法.
倍增建后缀数组:
以字符串s的每一位开始,取从某位开始长度为
接下来让我们来体会一下酸爽的代码.相信很多人都见到过构建后缀数组的代码,那十多个乱七八糟的for实在是让人蛋疼无比.
膜罗穗骞论文.论文中代码写法在很多方面都比刘汝佳训练指南写法要优.
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100010
char s[MAXN];
int A[MAXN];
//为了方便基数排序,我们把s中的字符转换成数字.s和A的下标使用统一0-n-1
int sa[MAXN],rank[MAXN];//最后结果的rank和sa
int Count[MAXN];//基数排序计数器
int l[MAXN],r[MAXN],tmp[MAXN];//基数排序共有两个关键字,r为第二关键字基数排序结果,l值即临时rank值.
int n,maxn;//计数上界
bool comp(int *A,int a,int b,int len)//字符串比较
{
return A[a]==A[b]&&A[a+len]==A[b+len];
}
int main()
{
/*省略读入等奇怪的过程*/
int i,j,k,*x=l,*y=r;//后面会整体交换l,r为了方便使用指针
for (i=0;i<maxn;i++) Count[i]=0;
for (i=0;i<n;i++) Count[x[i]=A[i]]++;//第一次基数排序
for (i=1;i<maxn;i++) Count[i]+=Count[i-1];
for (i=n-1;i>=0;i--) sa[--Count[x[i]]]=i;//初始的sa
for (k=1,i=1;i<n;k<<=1,maxn=i)
{
for (i=0,j=n-i;j<n;j++) y[i++]=i;//第二次基数排序
for (j=0;j<n;j++) if (sa[j]>=k) y[i++]=sa[j]-k;
for (j=0;j<n;j++) tmp[j]=x[y[j]];
for (j=0;j<maxn;j++) Count[j]=0;
for (j=0;j<n;j++) Count[tmp[j]]++;
for (j=1;j<maxn;j++) Count[j]+=Count[j-1];
for (j=n-1;j>=0;j--) sa[--Count[tmp[j]]]=y[j];//更新sa
for (swap(x,y),i=1,x[sa[0]]=0,j=1;j<n;j++) x[sa[j]]=comp(y,sa[j-1],sa[j],k)?i-1:i++;//更新rank
//在更新过程中可能有两字符串rank值相同,此时比较两字符串是否完全相同来区分rank值
//由于y数组在被用来更新sa后已经无用(下一次会重新求),节省空间使用y保存rank
}
}
SA的应用我准备在SAM之后单独写blog=- =(其实是懒得写在这里+骗分)