后缀自动机求本质不同串的数目

看了N年的后缀自动机才看懂。T.T还是我太菜了。

计算一个串中的不同子串数目, 后缀数组也能写。讲道理, 后缀自动机的复杂度应该低于倍增后缀数组算法。

结果发现几个算不同子串的后缀数组更快。QAQ(可能是n太小了, 但是1e7, 后缀自动机又开不下这么大数组, 测不了。)

归入正题

我们知道后缀自动机每个节点存的原串的子串, 且是连续的, 所以一个状态内的不同子串的数目即是

maxlen[i] - minlen[i] + 1, 我们又知道maxlen[lnk[i]] + 1 = minlen[i]

所以ans = \sum^{idx}_{i = 2}len[i] - len[link[i]]

#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#ifdef LOCAL
#define debug(x) cout << "[" __FUNCTION__ ": " #x " = " << (x) << "]\n"
#define TIME cout << "RuningTime: " << clock() << "ms\n", 0
#else
#define TIME 0
#endif
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
int len[N << 1];
int lnk[N << 1]; 
int cnt[N << 1]; 
int nxt[N << 1][26]; 
int idx; 
int last; 
ll sub[N << 1];
char s[1000010];
void init()
{
	last = idx = 1;
	lnk[1] = len[1] = 0;
}
void clear()
{
	memset(len, 0, sizeof len);
	memset(lnk, 0, sizeof lnk);
	memset(cnt, 0, sizeof cnt);
	memset(nxt, 0, sizeof nxt);
}
void extend(int c)
{
	int x = ++idx;
	len[x] = len[last] + 1; 
	sub[x] = 1;
	int p;
	for (p = last; p && !nxt[p][c]; p = lnk[p]) 
		nxt[p][c] = x;
	if (!p)   
		lnk[x] = 1, cnt[1]++;
	else
	{
		int q = nxt[p][c];
		if (len[p] + 1 == len[q]) 
			lnk[x] = q, cnt[q]++;		
		else  
		{ 
			int nq = ++idx;
			len[nq] = len[p] + 1; 
			lnk[nq] = lnk[q];
			memcpy(nxt[nq], nxt[q], sizeof nxt[q]);
			for (; p && nxt[p][c] == q; p = lnk[p]) 
				nxt[p][c] = nq;
			lnk[q] = lnk[x] = nq; 
			cnt[nq] += 2; 
		}
	}
	last = x;
}
void solve()
{
	ll ans = 0;
	for (int i = 2; i <= idx; i++)
		ans += len[i] - len[lnk[i]];
	cout << ans << endl;
}
int main()
{
#ifdef LOCAL
	freopen("D:/input.txt", "r", stdin);
#endif
	init(); 
	scanf("%s", s);
	int len = strlen(s);
	for (int i = 0; i < len; i++)
		extend(s[i] - 'a');
	solve();
	return TIME;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值