【P 2408】本质不同的字串的个数

1.题目链接。求一个字符串本质不同的字串有多少个?本质不同定义为:两个字符串不相等。

2.这个问题其实十分的简单,从后缀数组的角度来看,对于每一个sa[i],我们知道sa[i]代表排名为i的这个后缀所在的位置,假设是j,那么这个后缀的长度就是n-j.他有n-j个前缀,这些前缀都是这个字符串的子串,可以证明(所有的子串都是可以从这些后缀的前缀产生,从数量上就可以证明,二者相等)。但是我们从高度数组的定义又可以看出来,height[i]表示lcp(sa[i],sa[i-1]),所以i和i-1又会有公共的前缀,这些是重复的,减去即可。那么这个题就十分的简单了,ans=sigma(n-sa[i]-heigh[i])。时间复杂度O(nlogn).

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 2e5 + 10;
int sa[MAXN], rnk[MAXN], rsort[MAXN], y[MAXN], wr[MAXN], height[MAXN];
char a[MAXN];
bool cmp(int a, int b, int len)
{
	return wr[a] == wr[b] && wr[a + len] == wr[b + len];
}
void get_SA(int m, int n)
{
	for (int i = 1; i <= n; i++) rnk[i] = a[i - 1];
	for (int i = 1; i <= n; i++) rsort[rnk[i]]++;
	for (int i = 1; i <= m; i++) rsort[i] += rsort[i - 1];
	for (int i = n; i > 0; i--) sa[rsort[rnk[i]]--] = i;
	int len = 1, p = 0;
	while (p < n)
	{
		int k = 0;
		//y[i]:以第二关键字排名,排名为i的第一关键字的位置
		for (int i = n - len + 1; i <= n; i++) y[++k] = i;
		for (int i = 1; i <= n; i++) if (sa[i] > len) y[++k] = sa[i] - len;
		for (int i = 1; i <= n; i++) wr[i] = rnk[y[i]];
		//wr[i]:以第二关键字排序,排名为i的第一关键字的排名
		memset(rsort, 0, sizeof(rsort));
		for (int i = 1; i <= n; i++) rsort[wr[i]]++;
		for (int i = 1; i <= m; i++) rsort[i] += rsort[i - 1];
		for (int i = n; i > 0; i--) sa[rsort[wr[i]]--] = y[i];
		for (int i = 1; i <= n; i++) wr[i] = rnk[i];
		p = 1; rnk[sa[1]] = 1;
		for (int i = 2; i <= n; i++)
		{
			if (!cmp(sa[i], sa[i - 1], len)) p++;
			rnk[sa[i]] = p;
		}
		m = p; len <<= 1;
	}
}
void get_height(int n)
{
	int k = 0, j;
	for (int i = 1; i <= n; i++)
	{
		j = sa[rnk[i] - 1];
		if (k) k--;
		while (a[j + k - 1] == a[i + k - 1]) k++;
		height[rnk[i]] = k;
	}
}
ll solve(int n)
{
	ll ans = 0;
	for (int i = 1; i <= n; i++)
		ans += n + 1 - sa[i] - height[i];
	return ans;
}
int main()
{
	int len;
	scanf("%d", &len);
	scanf("%s", a);
	get_SA(300, len);
	get_height(len);
	printf("%lld", solve(len));
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值