spoj -705 New Distinct Substrings--后缀数组

本文介绍了一种使用后缀数组求解字符串中不同子串数量的方法。通过计算所有后缀间的不同前缀数量,得出答案。核心在于对新增后缀贡献的不同子串数目的计算,利用高度数组优化计算过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

每个子串一定是某个后缀的前缀,那么原问题等价于求所有后缀之间的不相

同的前缀的个数。如果所有的后缀按照 suffix(sa[1]), suffix(sa[2]),suffix(sa[3]), ...... ,suffix(sa[n])的顺序计算,不难发现,对于每一次新加进来的后缀 suffix(sa[k]),它将产生 n-sa[k]+1 个新的前缀。但是其中有height[k]个是和前面的字符串的前缀是相同的。所以 suffix(sa[k])将“贡献出 n-sa[k]+1- height[k]个不同的子串。累加后便是原问题的答案。这个做法的时间复杂度为 O(n)。 


//
//  main.cpp
//  spoj 705 New Distinct Substrings--后缀数组
//
//  Created by XD on 15/9/5.
//  Copyright (c) 2015年 XD. All rights reserved.
//

#include <iostream>
#include <string>
#include <queue>
#include <stack>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include<vector>
#include <string.h>
#include <string>
#include <algorithm>
#include <set>
#include <map>
#include <cstdio>
using namespace std ;
const int maxn = 50000 + 10 ;
int s[maxn] ;
int r[maxn] , t[maxn] , t1[maxn] ,height[maxn],c[maxn],sa[maxn] ;
void build_sa(int n , int m )
{
    int *x =t,*y = t1 ,*temp,i ;
    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(int k = 1 ; k<= n;k<<=1 )
    {
        int p = 0 ;
        for(i = n - k; i<n ;i++) y[p++] = i ;
        for(i = 0 ;i <n ; i++) if(sa[i] >= k ) y[p++] =sa[i]- k ;
        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>-1;i--) sa[--c[x[y[i]]]] = y[i] ;
        temp =x ; x = y ;y =temp ;
        p = 1 ; x[sa[0]] = 0 ;
        for(i = 1; i < n ; i++)
        {
            x[sa[i]] = (y[sa[i]]== y[sa[i-1]] && y[sa[i] + k ]== y[sa[i-1] + k ])? p-1:p++ ;
        }
        if (p >= n ) {
            break ;
        }
        m = p ;
    }
}
void getHeight(int n )
{
    for (int i = 0 ; i < n ; i++) {
        r[sa[i]] = i ;
    }
    int k = 0 ;
    for (int i = 0; i < n-1 ; i++) {
        if(k) k-- ;
        int j = sa[r[i]-1] ;
        while (s[i+k] == s[j+k]) {
            k++ ;
        }
        height[r[i]] = k ;
    }
}

int main(int argc, const char * argv[]) {
    int kase ; scanf("%d" ,&kase)  ;
    getchar() ;char t[maxn] ;
    while (kase--) {
        gets(t) ;
        int len = (int )strlen(t) ;
        for(int i = 0 ; i < len ; i++)
        {
            s[i] = t[i] ;
        }
        s[len] = 0 ;
        build_sa(len + 1, 128) ;
        getHeight(len+1) ;
        int ans = 0;
        for (int i = 1 ; i < len+1 ; i++) {
            ans += (len - sa[i] - height[i]) ;
        }
        printf("%d\n" ,ans) ;
    }
    return 0;
}















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值