后缀数组模板和最最最常用的思路

虽然不能理解,但好歹做个模板,方便以后copy23333

#include <bits/stdc++.h>
using namespace std;

/*
   1.判断一个串s是否为p的后缀
   2.多模式串匹配
   3.求原串的后缀的最长公共前缀\
*/

const int maxn=10000+10;
char s[maxn];//原串
int sa[maxn],t[maxn],t2[maxn],c[maxn],n;//sa就是后缀数组
//构造后缀数组
int Rank[maxn],height[maxn];//rank:后缀在sa数组中的下标,height:sa[i-1]和sa[i]的最长公共前缀
//预处理height
void getHeight(){
    int i,j,k = 0;
    for(i = 0; i < n; i++) Rank[sa[i]] = i;
    for(i = 0; i < n; i++)
    {
        if(k) k--;
        j = sa[Rank[i]-1];
        while(s[i+k] == s[j+k]) k++;
        height[Rank[i]] = k;
    }
}

void build_sa(int m){
    int i,*x=t,*y=t2;
    //基数排序
    for(int i=0;i<m;i++)c[i]=0;
    for(int i=0;i<n;i++)c[x[i]=s[i]]++;
    for(int i=1;i<m;i++)c[i]+=c[i-1];
    for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
    for(int k=1;k<=n;k++){
        int p=0;
        //利用sa数组排序第二关键字
        for(int i=n-k;i<n;i++)y[p++]=i;
        for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
        //基数排序第一关键字
        for(int i=0;i<m;i++)c[i]=0;
        for(int i=0;i<n;i++)c[x[y[i]]]++;
        for(int i=0;i<m;i++)c[i]+=c[i-1];
        for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for(int i=1;i<n;i++){
            x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p+1;
        }
        if(p>=n)break;//以后即使继续倍增,也不会改变sa
        m=p;//下次基数排序的最大值
    }
    getHeight();//在这里调用,预处理height数组
}
int m;
//找出串p在原串中的一个位置
int cmp_suffix(char *pattern,int p){
    return strncmp(pattern,s+sa[p],m);
}
int find(char *p){
    m=strlen(p);
    if(cmp_suffix(p,0)<0)return -1;
    if(cmp_suffix(p,n-1)>0)return -1;
    int L=0,R=n-1;
    while(R>=L){//二分查找
        int M=L+(R-L)/2;
        int res=cmp_suffix(p,M);
        if(!res)return M;
        if(res<0)R=M-1;
        else L=M+1;
    }
    return -1;//找不到
}
/*
    前面处理完height数组,还需要RMQ预处理和查询
*/
int RMQ[maxn], mm[maxn], best[20][maxn];
//预处理
void initRMQ(int n)
{
    mm[0] = -1;
    for(int i = 1; i <= n; i++)
        mm[i] = ((i&(i-1))==0) ? mm[i-1]+1 : mm[i-1];
    for(int i = 1; i <= n; i++) best[0][i] = i;
    for(int i = 1; i <= mm[n]; i++)
        for(int j = 1; j+(1<<i)-1 <= n; j++)
        {
            int a = best[i-1][j];
            int b = best[i-1][j + (1<<(i-1))];
            if(RMQ[a] < RMQ[b]) best[i][j] = a;
            else best[i][j] = b;
        }
}
//查询后缀a,b的LCP
int askRMQ(int a, int b)
{
    int t;
    t = mm[b-a+1];
    b -= (1<<t)-1;
    a = best[t][a]; b = best[t][b];
    return RMQ[a] < RMQ[b] ? a : b;
}
//查询后缀a,b的LCP
int lcp(int a, int b)
{
    a = Rank[a], b = Rank[b];
    if(a > b) swap(a, b);
    return height[askRMQ(a+1, b)];
}

//找出一个重复出现k次的子串的个数,后缀数组+lcp+二分
bool judge(int l, int k, int n)
{
    int cnt = 0;
    for(int i = 2;  i <= n; i++)
    {
    	if(height[i] < l) cnt = 0;
    	else {
    		cnt++;
    		if(cnt+1 >= k) return true;
    	}
    }
    return false;
}
/**
给你一个长为N的字符串,求不同的子串的个数?
对于一个后缀sa[i],它产生了n-sa[i]个前缀,减去height[i]个相同的前缀(与前一个比较),
则产生了n-sa[i]-height[i]个子串。累加后即结果。
*/
void solve() {
    long long ans = 0;
    for(int i = 1; i <= n; i++ ) {
        ans += n + 1 - sa[i] - height[i];
    }
    cout << ans << endl;
}
/*
   类似的有,给定n个串,求至少出现在k个串中的最长字串
   这个问题和;蓝书上P223的UVA11107相似,可以参考此题
*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值