洛谷3809 SA模板 后缀数组学习笔记(复习)

其实SA这个东西很久之前就听过qwq
但是基本已经忘的差不多了

嘤嘤嘤
QWQ感觉自己不是很理解啊
所以写不出来那种博客
QWQ只能安利一些别人的博客了



真的是讲的非常好
不要在意名字
orz,膜拜他们

顺便弄上自己的代码(里面有一些需要注意的地方)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 2e6+1e2;
int wb[maxn];
int rk[maxn];
int sa[maxn],tmp[maxn];
char a[maxn];
int h[maxn],height[maxn];
int n;
void getsa()
{
 int *x=rk,*y=tmp;
 int s = 128;
 for (int i=1;i<=n;i++) x[i]=a[i],y[i]=i; //初始每个长度为1的后缀的rank是他自己的字符大小,第二关键字相当于空,那么就顺次赋值为i 
 for (int i=1;i<=s;i++) wb[i] =0;
 for (int i=1;i<=n;i++) wb[x[y[i]]]++; // 这里其实基数排序的时候,x表示上一轮的rank,y[i]表示第二关键字排名为i的第一关键字的位置是多少
    for (int i=1;i<=s;i++) wb[i]+=wb[i-1];//做前缀和就能更好的算出来排名,比如说有3个a,2个b,那么自然第一个b的排名就要从4开始 
 for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i]; //只能感性理解了啊qwq之所以倒着枚举是为了保证在第一关键字相同的时候,第二关键字也是有序的 
 int p = 0;
 for (int j=1;p<n;j<<=1) //p是指本质不同的串的个数 
 {
  //x表示上一轮的rank
  //y表示排名为i的第二关键字的第一关键字的位置是多少(空优先) 
  p=0;
  //这里可以这么理解,如果一个串他的位置是大于n-j+1,那么他一定是没有第二关键字的。 
  for (int i=n-j+1;i<=n;i++) y[++p]=i; //第二关键字为空,就排名靠前 
     for (int i=1;i<=n;i++) if (sa[i]>j) y[++p]=sa[i]-j; //如果排名为i的位置是大于j的,那么他可以成为一个第二关键字,并且第一关键字的位置应该是sa[i]-j;
  for (int i=1;i<=s;i++) wb[i]=0;
  for (int i=1;i<=n;i++) wb[x[y[i]]]++;
  for (int i=1;i<=s;i++) wb[i]+=wb[i-1];
  for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i]; //这里i之所以从n开始,因为我们要保证排序第一关键字的时候,第二关键字一定也是符合原来的顺序的,就是说,原来第二关键字大的,一定在后面(这个是基数排序的思想)
  swap(x,y);//交换之后,y表示上一轮的rank,x是一个新的数组 
  p=1;
  x[sa[1]]=1;
  //若两个串的两部分在上一轮rank都相等的话, 那么无法分辨,所以p不用加 
  for (int i=2;i<=n;i++) x[sa[i]] = (y[sa[i]]==y[sa[i-1]] && y[sa[i-1]+j]==y[sa[i]+j]) ? p : ++p;
  s=p;
 } 
 for (int i=1;i<=n;i++) rk[sa[i]]=i;
 h[0]=0;
 for (int i=1;i<=n;i++)
 {
  //h[i]表示i号后缀与它前一名的后缀的最长公共前缀 
  //height[i]表示排名为i的后缀和排名为i-1的后缀的lcp 
  h[i]=max(h[i-1]-1,0);
  while (i+h[i]<=n && sa[rk[i]-1]+h[i]<=n && a[i+h[i]]==a[sa[rk[i]-1]+h[i]])
    h[i]++;
 }
 for (int i=1;i<=n;i++) height[i]=h[sa[i]];
}
int main()
{
  scanf("%s",a+1);
  n=strlen(a+1);
  getsa();
  for (int i=1;i<=n;i++) cout<<sa[i]<<" ";
  return 0;
}

Update

整理一些 S A SA SA的小性质和经典应用。(会持续更新的)

1.求两个后缀的 l c p lcp lcp ,应该是 m i n ( h e i g h t [ r k [ i ] + 1 ] , h e i g h t [ r k [ i ] + 2 ] . . . . . h e i g h t [ r k [ j ] ] ) min(height[rk[i]+1],height[rk[i]+2].....height[rk[j]]) min(height[rk[i]+1],height[rk[i]+2].....height[rk[j]])

2.对于排名为 i i i的后缀,与它 l c p lcp lcp最长的后缀应该是排名为 i − 1 i-1 i1,(可以理解为越靠前差异越多,越靠前,取 m i n min min的区间就越长)

3.最长可重叠重复子串,应该是 m a x ( h e i g h t [ i ] ) max(height[i]) max(height[i])(这里把子串看成后缀的前缀,同时依据性质2就能得到)

4.给定一个子串,求不相同子串的个数,这里要这么考虑,按照字典序加入,每加入一个字符串,会新增加 n − s a [ i ] + 1 n-sa[i]+1 nsa[i]+1个新的子串,但是会重复 h e i g h t [ i ] height[i] height[i]个, ( 只 有 l c p 会 重 复 , 同 时 依 据 性 质 2 ) (只有lcp会重复,同时依据性质2) lcp2)

5.给定两个串,求他们的最长公共子串。
将B串拼到A串后面,然后中间添加一个非法字符,然后直接想询问最大的lcp(保证 s a [ i ] 和 s a [ i − 1 ] sa[i]和sa[i-1] sa[i]sa[i1]分别位于两个串即可)

6.给定两个串,求他们的公共子串数目。

将B串拼到A串后面,然后中间添加一个非法字符,然后对于每个 h e i g h t height height用单调栈维护出左右最远能扩展到哪里。然后 a n s ans ans加上 h e i g h t [ i ] ∗ ( g e t a ( i − 1 , l [ i ] − 1 ) ∗ g e t b ( r [ i ] , i ) + g e t b ( i − 1 , l [ i ] − 1 ) ∗ g e t a ( r [ i ] , i ) ) height[i]*(geta(i-1,l[i]-1)*getb(r[i],i)+getb(i-1,l[i]-1)*geta(r[i],i)) height[i](geta(i1,l[i]1)getb(r[i],i)+getb(i1,l[i]1)geta(r[i],i))

这里之所以是这个式子的原因(第一要保证是一个端点属于A串,一个属于B串。另一个原因是因为对于一个扩展区间 [ l , p o s , r ] [l,pos,r] [l,pos,r]来说,选择后缀的右端点是在 [ p o s , r ] [pos,r] [pos,r]而左端点是 [ l − 1 , p o s − 1 ] [l-1,pos-1] [l1,pos1],因为后缀的选择的左边对于 h e i g h t height height是开区间,参考性质1。

基于SSM框架的智能家政保洁预约系统,是一个旨在提高家政保洁服务预约效率和管理水平的平台。该系统通过集成现代信息技术,为家政公司、家政服务人员和消费者提供了一个便捷的在线预约和管理系统。 系统的主要功能包括: 1. **用户管理**:允许消费者注册、登录,并管理他们的个人资料和预约历史。 2. **家政人员管理**:家政服务人员可以注册并更新自己的个人信息、服务类别和服务时间。 3. **服务预约**:消费者可以浏览不同的家政服务选项,选择合适的服务人员,并在线预约服务。 4. **订单管理**:系统支持订单的创建、跟踪和管理,包括订单的确认、完成和评价。 5. **评价系统**:消费者可以在家政服务完成后对服务进行评价,帮助提高服务质量和透明度。 6. **后台管理**:管理员可以管理用户、家政人员信息、服务类别、预约订单以及处理用户反馈。 系统采用Java语言开发,使用MySQL数据库进行数据存储,通过B/S架构实现用户与服务的在线交互。系统设计考虑了不同用户角色的需求,包括管理员、家政服务人员和普通用户,每个角色都有相应的权限和功能。此外,系统还采用了软件组件化、精化体系结构、分离逻辑和数据等方法,以便于未来的系统升级和维护。 智能家政保洁预约系统通过提供一个集中的平台,不仅方便了消费者的预约和管理,也为家政服务人员提供了一个展示和推广自己服务的机会。同时,系统的后台管理功能为家政公司提供了强大的数据支持和决策辅助,有助于提高服务质量和管理效率。该系统的设计与实现,标志着家政保洁服务向现代化和网络化的转型,为管理决策和控制提供保障,是行业发展中的重要里程碑。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值