后缀数组--倍增算法模板+Distinct Substrings (后缀数组求字符串的不同子串) SPOJ - DISUBSTR

我感觉后缀数组算法不好理解,我是看的 2009年国家集训队论文 《后缀数组---处理字符串的有力工具》来学的后缀数组算法。

这里只是附上倍增算法的模板,不对算法进行讲解。

int wa[maxn],wb[maxn],wv[maxn],w[maxn];
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++) w[i]=0;
    for(i=0;i<n;i++) w[x[i]=r[i]]++;
    for(i=1;i<m;i++) w[i]+=w[i-1];
    for(i=n-1;i>=0;i--) sa[--w[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++) w[i]=0;
        for(i=0;i<n;i++) w[wv[i]]++;
        for(i=1;i<m;i++) w[i]+=w[i-1];
        for(i=n-1;i>=0;i--) sa[--w[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++;
    }
    return;
}

 

下面是后缀数组的一个应用题目

题目链接:https://vjudge.net/problem/SPOJ-DISUBSTR

                                                   Distinct Substrings

Given a string, we need to find the total number of its distinct substrings.

Input

T- number of test cases. T<=20;
Each test case consists of one string, whose length is <= 1000

Output

For each test case output one number saying the number of distinct substrings.

Example

Sample Input:
2
CCCCC
ABABA

Sample Output:
5
9

Explanation for the testcase with string ABABA: 
len=1 : A,B
len=2 : AB,BA
len=3 : ABA,BAB
len=4 : ABAB,BABA
len=5 : ABABA
Thus, total number of distinct substrings is 9.

题目大意:给你一个字符串,求这个字符串有多少个不同的子字符串。 

思路:直接根据09年oi论文<<后缀数组——出来字符串的有力工具>>的解法。 

还有另一种思想:总数为n*(n-1)/2,height[i]是两个后缀的最长公共前缀,所以用总数-height[i]的和就是答案

注:拥有相同长的lcp的两个后缀一定挨在一起

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int maxn=1e3+5;
int T;
char s[maxn];
int f[maxn],suf[maxn];
int rank[maxn],height[maxn];
//模板 
int wa[maxn],wb[maxn],wv[maxn],w[maxn];
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++) w[i]=0;
    for(i=0;i<n;i++) w[x[i]=r[i]]++;
    for(i=1;i<m;i++) w[i]+=w[i-1];
    for(i=n-1;i>=0;i--) sa[--w[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++) w[i]=0;
        for(i=0;i<n;i++) w[wv[i]]++;
        for(i=1;i<m;i++) w[i]+=w[i-1];
        for(i=n-1;i>=0;i--) sa[--w[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++;
        m=p;
    }
    return;
}

int main()
{
	cin>>T;
	while(T--)
	{
		scanf("%s",s);
		int cnt=strlen(s);
		int i,j,k;
		for(i=0;i<cnt;i++)
		{
			f[i]=s[i];
		}
		f[cnt]=0;
		da(f,suf,cnt+1,256);
		for(i=1;i<=cnt;i++)
		{
			rank[suf[i]]=i;
		}
		for(k=0,i=0;i<cnt;i++){
			if(k)k--;
			j=suf[rank[i]-1];
			while(f[i+k]==f[j+k])k++;
			height[i]=k;//在这个题目里,这样写也可 
		}
		int sum=0;
		for(i=0;i<cnt;i++)
		{
			sum=sum+cnt-i-height[i];
		}
		printf("%d\n",sum);
	}
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值