您的位置:首页 > 其它

New Distinct Substrings (后缀数组,统计有多少个不同的子串)

2016-08-15 18:16 405 查看
首先要确认一个事实:任何一个子串都是一个后缀的前缀。

根据这个,首先处理出height数组。

对于样例:ABABA

后缀数组处理出来是这样的结果。

A 0

ABA 1

ABABA 3

BA 0

BABA 2

右边的数字是height数组的值。

因为:任何一个子串都是一个后缀的前缀。 所以对于每个后缀就有 n-sa[i]个子串。

这时候从第一个后缀出来的答案是1。

然后看第二个,height是1,意味着A是和上面那个重复的,所以在计算这个的子串的时候,我们需要在这个基础上,把这个多算的A个减去。

第三个与第二个同理,height是3,意味着ABA都是重复的,所以需要减去3(ABA,AB,A 这3个多出来了)。

这个A看上去减了2遍,事实上这样做答案才会正确。因为既然这个height大于1了,就意味着第一个字符是重复的,而这个重复的字符一定在上面被算过了,所以不需要再计算。

最后的答案就是 把每个的 n-sa[i]-height[i]加了起来。

(代码中是n-1-sa[i]-height[i],这是因为对于后缀数组模板的修正,后面加一个比一切字符都小的字符)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int nMax = 1200001;

char arr[nMax+1];
int sa[nMax], rank[nMax], height[nMax];
int wa[nMax], wb[nMax], wv[nMax], wd[nMax];
int n,k;
int cmp(int *r, int a, int b, int l){
return r[a] == r[b] && r[a+l] == r[b+l];
}

void da(char *r, int n, int m){          //  倍增算法 r为待匹配数组  n为总长度 m为字符范围
int i, j, p, *x = wa, *y = wb, *t;
for(i = 0; i < m; i ++) wd[i] = 0;
for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++;
for(i = 1; i < m; i ++) wd[i] += wd[i-1];
for(i = n-1; i >= 0; i --) sa[-- wd[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 ++) wd[i] = 0;
for(i = 0; i < n; i ++) wd[wv[i]] ++;
for(i = 1; i < m; i ++) wd[i] += wd[i-1];
for(i = n-1; i >= 0; i --) sa[-- wd[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 ++;
}
}
}

void calHeight(char *r, int n){           //  求height数组。
int i, j, k = 0;
for(i = 1; i <= n; i ++)
{
rank[sa[i]] = i;
//cout<<sa[i]<<endl;
}
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 ++);
}
}

int main()
{
int t;
cin>>t;
while(t--)
{
scanf("%s",arr);
int len=strlen(arr);
arr[len]=0;
arr[len+1]='\0';
len++;

da(arr,len,140);
calHeight(arr,len-1);

int ans=0;
for(int i=1;i<=len-1;i++)
{
ans+=len-1-sa[i]-height[i];
}
cout<<ans<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: