后缀数组

点击打开链接

应用

1、可重叠最长重复子串

做法比较简单,只需要求height数组中的最大值即可。首先求最长重复子串,等价于求两个后缀的最长公共前缀的最大值。因为任意两个后缀的最长公共前缀都是height数组里的某一段的最小值,那么这个值一定不大于height数组里的最大值。所以最长重复子串的长度就是height数组中的最大值。这个做法的时间复杂度为O(n);

2、不可重叠最长重复子串

先二分答案,把题目变成判定性问题:判断是否存在两个长度为k的子串是相同的,且不重叠。解决这个问题的关键还是利用height数组。把排序后的后缀分成若干组,其中每组的后缀之间的height值都不小于k。

bool judge(int mid,int n){  
    int cas = 2;  
    while(true){  
        while(cas<=n && height[cas] < mid) cas++;  
        if(cas > n) return false;  
        int mn = sa[cas-1];  
        int mx = sa[cas-1];  
        while(cas<=n && height[cas] >= mid){  
            mn = min(mn,sa[cas]);  
            mx = max(mx,sa[cas]);  
            cas++;  
            if(mx-mn >= mid) return true;  
        }  
    }  
    return false;  
}  
3、可重叠的k次最长重复子串

给定一个字符串,求至少出现k次的最长重复子串,这k个子串可以重叠

先二分答案,然后将后缀分成若干组。不同的是,这里要判断的是有没有一个组的后缀个数不小于k。如果有,那么存在k个相同的子串满足条件,否则不存在。

bool judge(int mid,int n,int k){
    int cas = 2;
    while(true){
        while(cas<=n && height[cas] < mid) cas++;
        if(cas > n) return false;
        int cnt = 1;
        while(cas<=n && height[cas] >= mid){
            cnt++;
            cas++;
        }
        if(cnt >= k) return true;
    }
    return false;
}

4、不相同的子串的个数

所有子串都是某个后缀的前缀,那么对于每一个后缀,使他的起始位置为sa[i],那么它对答案的贡献是n - sa[i];然而还有重复的。这个sa[i]与sa[i - 1]的LCP是height[i],那么说明sa[i -1]也有对应height个后缀已经计入答案,所以实际对答案的贡献是N - sa[i] - height[i];

int solve(int n){
    int res = 0;
    for(int i = 1; i <= n; ++i){
        res += n-sa[i]-height[i];
    }
    return res;
}
5、最长回文子串

穷举每一位,然后计算以这个字符为中心的最长回文子串。注意这里要分两种情况:一是回文子串的长度为奇数,二是长度为偶数。两种情况都可以转化为求一个后缀和一个反过来写的后缀的最长公共前缀。具体的做法是:将整个字符串反过来写在原字符串后面,中间用一个特殊的字符隔开。这样就把问题变为了求这个新的字符串的某两个后缀的最长公共前缀。这个做法的时间复杂度为O(nlogn);


DC3 。 空间复杂度O3N 时间复杂度On

#define F(x) ((x) / 3 + ((x) % 3 == 1 ? 0 : tb))
#define G(x) ((x) < tb ? (x) * 3 + 1 :((x) - tb) * 3 + 2)
const int MAXN = 300010;
const int MAXM = 100010;
char input[MAXM];
int wa[MAXN],wb[MAXN],ws[MAXN],wv[MAXN],wsd[MAXN],r[MAXN],sa[MAXN];
char str[MAXN];
int dp[MAXM][25];
int c0(int *r,int a,int b)
{return r[a] == r[b] && r[a + 1] == r[b + 1] && r[a + 2] == r[b + 2];}
int c12(int k,int *r,int a,int b)
{if(k == 2) return r[a] < r[b] || r[a] == r[b] && c12(1,r,a + 1,b + 1);
else return r[a] < r[b] || r[a] == r[b] && wv[a + 1]< wv[b + 1];}
void sort(int *r,int *a,int *b,int n,int m)
{
    int i;
    for(i = 0 ; i < n ; i++) wv[i] = r[a[i]];
    for(i = 0 ; i < m ; i++) wsd[i] = 0;
    for(i = 0 ; i < n ; i++) wsd[wv[i]]++;
    for(i = 1 ; i < m ; i++) wsd[i] += wsd[i - 1];
    for(i = n - 1 ; i >= 0 ; i--) b[--wsd[wv[i]]] = a[i];
}

void dc3(int *r,int *sa,int n,int m)
{
    int i,j,*rn = r + n ,*san = sa + n,ta = 0,tb = (n + 1) / 3,tbc = 0,p;
    r[n] = r[n + 1] = 0;
    for(i = 0 ; i < n ; i++) if(i % 3 != 0) wa[tbc++] = i;
    sort(r + 2,wa,wb,tbc,m);
    sort(r + 1,wb,wa,tbc,m);
    sort(r,wa,wb,tbc,m);
    for(p = 1,rn[F(wb[0])] = 0,i = 1 ; i < tbc ; i++)
        rn[F(wb[i])] = c0(r,wb[i - 1],wb[i])?p - 1 : p++;
    if(p < tbc) dc3(rn,san,tbc,p);
    else for(i = 0 ; i < tbc ; i++) san[rn[i]] = i;
    for(i = 0 ;i < tbc ; i++) if(san[i] < tb) wb[ta++] = san[i] * 3;
    if(n % 3 == 1) wb[ta++] = n - 1;
    sort(r,wb,wa,ta,m);
    for(i = 0 ; i < tbc ; i++) wv[wb[i] = G(san[i])] = i;
    for(i = 0,j = 0,p = 0 ; i < ta && j < tbc ; p++)
        sa[p]=c12(wb[j] % 3,r,wa[i],wb[j]) ? wa[i++] : wb[j++];
    for(;i < ta ; p++) sa[p] = wa[i++];
    for(;j < tbc ; p++) sa[p] = wb[j++];
}


int cmp(int *r,int a,int b,int l)
{return r[a ]== r[b] && r[a + l] == r[b + l];}
void da(int *r,int *sa,int n,int m)
{
    int i,j,p,*x = wa,*y = wb,*t;
    for(i = 0 ; i < m ; i++) wsd[i] = 0;
    for(i = 0 ; i < n ; i++) wsd[x[i] = r[i]]++;
    for(i = 1 ; i < m ; i++) wsd[i] += wsd[i - 1];
    for(i = n - 1 ; i >= 0 ; i--) sa[--wsd[x[i]]] = i;
    for(j = 1 , p = 1 ; p < n ; j *= 2,m = p)
    {
        for(p = 0 ,i = n - j ; i < n ; i++) y[p++] = i;
        for(i = 0 ; i < n ; i++) if(sa[i] >= j) y[p++] = sa[i] - j;
        for(i = 0 ; i < n ; i++) wv[i] = x[y[i]];
        for(i = 0 ; i < m ; i++) wsd[i] = 0;
        for(i = 0 ; i < n ; i++) wsd[wv[i]]++;
        for(i = 1 ; i < m ; i++) wsd[i] += wsd[i - 1];
        for(i = n - 1 ; i >= 0 ; i--) sa[--wsd[wv[i]]] = y[i];
        for(t = x,x = y,y = t,p = 1,x[sa[0]] = 0,i = 1;i < n ; i++)
            x[sa[i]] = cmp(y,sa[i - 1],sa[i],j) ? p - 1 : p++;
    }
}

int Rank[MAXN],height[MAXN];
void calheight(int *r,int *sa,int n)
{
    int i,j,k = 0;
    for(i = 1 ; i <= n ; i++) Rank[sa[i]] = i;
    for(i = 0 ; i < n ; height[Rank[i++]] = k)
    for(k ? k--:0,j = sa[Rank[i] - 1] ;r[i + k]==r[j + k];k++);
}

void RMQ_init(int n,int b[])
{
    int i,j;
    for(i = 1 ; i <= n ; i++)
        dp[i][0] = b[i];
    for(j = 1 ; (1 << j) <= n ; j++)
        for(i = 1 ; i + (1 << j) - 1 <= n ; i++)
            dp[i][j] = min(dp[i][j - 1],dp[i + (1<<(j - 1))][j - 1]);
}

int rmq(int s,int v)
{
    s = Rank[s],v = Rank[v];
    if(s > v)swap(s,v);
    s++;
    int k=(int)(log((v - s + 1)*1.0)/log(2.0));
    return min(dp[s][k],dp[v - (1 << k) + 1][k]);
}


倍增 空间复杂度ON,时间复杂度ONLOGN

int sa[MAXN],r[MAXN];
int t1[MAXN],t2[MAXN],c[MAXN];
int Rank[MAXN],height[MAXN];

void build_sa(int s[],int n,int m)
{
    int i,j,p,*x = t1,*y = t2;
    for(i = 0 ; i < m ; i++) c[i] = 0;
    for(i = 0 ; i < n ; i++) c[x[i] = s[i]]++;
    for(i = 1 ; i < m ; i++) c[i] += c[i - 1];
    for(i = n - 1 ; i >= 0 ; i--) sa[--c[x[i]]] = i;
    for(j = 1 ; j <= n ; j <<= 1)
    {
        p=0;
        for(i = n - j ; i < n ; i++) y[p++] = i;
        for(i = 0 ; i < n ; i++) if(sa[i] >= j) y[p++] = sa[i] - j;
        for(i = 0 ; i < m ; i++) c[i] = 0;
        for(i = 0 ; i < n ; i++) c[x[y[i]]]++;
        for(i = 1 ; i < m ; i++) c[i]+=c[i-1];
        for(i = n - 1 ; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
        swap(x,y);
        p = 1;
        x[sa[0]] = 0;
        for(i = 1 ; i < n ; i++)
            x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j] ? p - 1 : p++;
        if(p >= n)break;
        m = p;
    }
}

void getHeight(int s[],int n)
{
    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;
    }
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值