后缀数组板子

学习后缀数组参考这两个博客https://www.cnblogs.com/Sparks-Pion/p/9558888.html

https://www.cnblogs.com/GDOI2018/p/10292378.html

看不懂没关系,背个板子天下无敌  23333

#include <bits/stdc++.h>
using namespace std;
const int N=1e6 + 5;
int n,m,rak[N],sa[N],buc[N],id[N],height[N]; 
char s[N];
/*------------------------------------------------------------
rank[i] 第i个后缀的排名; 
sa[i] 排名为i的后缀位置; 
buc[i] 计数排序辅助数`
组;
height[i] 排名为i的后缀与排名为(i-1)的后缀的LCP;
id[i] 倍增中后半段字符串位置(计数排序中的第二关键字);
s为原串
------------------------------------------------------------*/
inline void qsort(){
    //rak第一关键字,id第二关键字。
    for(int i=0;i<=m;++i) buc[i]=0;
    for(int i=1;i<=n;++i) ++buc[rak[id[i]]];
    for(int i=1;i<=m;++i) buc[i]+=buc[i-1];
    for(int i=n;i>=1;--i) sa[buc[rak[id[i]]]--]=id[i]; 
    //计数排序,把新的二元组排序
}
//通过二元组两个下标的比较,确定两个子串是否相同
inline int cmp(int x,int y,int l){return id[x]==id[y]&&id[x+l]==id[y+l];}
int main() {
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;++i) rak[i]=s[i],id[i]=i;
    m=127,qsort();//一开始是以单个字符为单位,所以(m = 127)
    for(int l=1,p=1,i;p<n;l<<=1,m=p){
        //l 当前一个子串的长度; m 当前离散后的排名种类数
        //当前的id(第二关键字)可直接由上一次的sa的得到
        //更新sa值,并用id暂时存下上一轮的rak(用于cmp比较)
        for(p=0,i=n-l+1;i<=n;++i) id[++p]=i;//长度越界,第二关键字为0
        for(i=1;i<=n;++i) if (sa[i]>l) id[++p]=sa[i]-l;
        qsort(),swap(rak,id),rak[sa[1]]=p=1;
        //用已经完成的SA来更新与它互逆的rak,并离散rak
        for(i=2;i<=n;++i) rak[sa[i]]=cmp(sa[i],sa[i-1],l)?p:++p;
    }
    
    
    for(int i = 1; i <= n; i ++)cout << sa[i] << " ";
    cout << endl;
    //LCP  这个知道原理后就比较好理解程序
    int j,k=0;
    for(int i=1;i<=n;height[rak[i++]]=k)
    for(k=k?k-1:k,j=sa[rak[i]-1];s[i+k]==s[j+k];++k);
    return 0;
}

 

#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
 
typedef long double ld;
  
const ll maxn = 1e6 + 5;
 
const ll mod = 1e9 + 7;

ll n,m,rak[maxn],id[maxn],sa[maxn],buc[maxn],height[maxn];
char s[maxn];
void qsort()
{
    for(int i = 0; i <= m; i ++)buc[i] = 0;
    for(int i = 1; i <= n; i ++)++buc[rak[id[i]]];
    for(int i = 1; i <= m; i ++)buc[i] += buc[i - 1];
    for(int i = n; i >= 1; i --)sa[buc[rak[id[i]]]--] = id[i];
}

ll cmp(ll x,ll y,ll l)
{
    return id[x] == id[y] && id[x + l] == id[y + l];
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    
    cin >> (s + 1);
    n = strlen(s + 1);
    for(int i = 1; i <= n; i ++)rak[i] = s[i],id[i] = i;
    m = 127,qsort();
    for(int l = 1, p = 1,i; p < n; l <<= 1, m = p)
    {
        for(p = 0,i = n - l + 1; i <= n; i ++)id[++p] = i;
        for(i = 1; i <= n; i ++)if(sa[i] > l)id[++p] = sa[i] - l;
        qsort(),swap(rak,id),rak[sa[1]] = p = 1;
        for(i = 2; i <= n; i ++)rak[sa[i]] = cmp(sa[i],sa[i - 1],l) ? p : ++p;
    }
	
    for(int i = 1; i <= n; i ++)cout << sa[i] << " ";
    cout << endl;
	
    ll k = 0;
    for(int i = 1; i <= n; i ++)
    {
        if(k)k--;
        ll j = sa[rak[i] - 1];
        while(s[i + k] == s[j + k])k++;
        height[rak[i]] = k;
    }
    return 0;
}
    

后缀数组应用

两个后缀的最大公共前缀

lcp(x,y)=min(heigh[x−y])lcp(x,y)=min(heigh[x−y]), 用rmq维护,O(1)查询

可重叠最长重复子串

Height数组里的最大值

不可重叠最长重复子串 POJ1743

首先二分答案xx,对height数组进行分组,保证每一组的minheightminheight都>=x>=x

依次枚举每一组,记录下最大和最小长度,多sa[mx]−sa[mi]>=xsa[mx]−sa[mi]>=x那么可以更新答案

本质不同的子串的数量

枚举每一个后缀,第ii个后缀对答案的贡献为len−sa[i]+1−height[i]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值