后缀自动机————入门


学后缀自动机就是看图看图再看图,没图的教程你想学后缀自动机是不阔能的。。。。陈老师的教程之所以难懂就是因为图少,概念多,其实我觉得并不需要什么慨念就能学好啊!!!!!!后缀自动机其实很简单的。。。。看图看图!!!!!!!
http://blog.sina.com.cn/s/blog_70811e1a01014dkz.html(主要看图)

http://blog.csdn.net/liyuanshuo_nuc/article/details/53561527(看后缀连接作用)

http://blog.csdn.net/qq_35649707/article/details/66473069(看算法描述)如果要学怎样构建后缀树最好把图看好,不过之前百度下什么是后缀树哦!!(俄国教程)

看着三个博客了解到怎样构造后缀自动机,并注意后缀连接的作用。
我的图,照着那个俄国教程画的,只不过我画的是hiho上的那种形式,两个结合看最好。。同时一定要看后缀树,还有就是后缀链接的性质:fa(max)+1=son(min)!!!!!!!!

后缀树上我画的点都是代表最长串的,其他一些串没画出来。你把后缀链接想成树边就好了,原点是根

其实我觉得学后缀自动机的最好方法就是多看下教程里的图是最好的。。。。。。。。
max:代表这个节点表示的最长子串。min:代表这个节点表示的最短子串。连续转移:从max->max+1相对应的就是非连续转移;
1.关于线性构建后缀自动机的证明:
很明显连续转移就只有o(n)个上面的教程很清楚了
其实你就是考察每条非连续转移就好了 记这条非连续转移是p->q 考察 原点->p(最长路径)   p->q  q->结尾点(最长路径)  组成的总路径, 这个总路径对应一个字符串的后缀。
当然还有个重要性质就是如果 p->q这个不连续转移不同那么这个总路径就是不同的。假设这个结论不正确,那么一个总路径上就会有两个非连续转移,但是我们知道如果有非连续转移 p->q出现,是因为有从原点->某点->q 其中 某点->q 以及 原点->某点,这些路径上都是连续转移,所以才会有非连续转移p->q,
那么根据我们的定义总路径上是不会存在两个非连续转移的,所以每个非连续转移对应一个后缀,所以最多有n个非连续转移。
现在线性构造就很好说了,随便yy下。。。
2.后缀链接的性质是 fa(max)+1=son(min);//根据这个性质我们阔以在构造好后缀自机后构造后缀树。这也是我第一遍看教程时忽略的一个重要性质!!!!!
3.每个点其实是代表一些串的集合,这个hiho上已经说的很清楚了。后缀链接就相当与对这些集合进行分割。由于一些集合进行分割后有一些公共的串,这个分割出来的公共的串组成的集合就是后缀树上的一些父亲,所以我们在求最长公共前缀什么的,时候就阔以利用这些性质。
#include<iostream>
#include<cstdio>
#include<map>
#include<vector>
using namespace std;
struct node
{
	map<int, int>son;
	int fa, maxx;
};
node tree[200020];
int root = 0;
int last=0;
int tot=1;
long long ans = 0;
int cal(int a)
{
	return tree[a].maxx - tree[tree[a].fa].maxx;
}
void insert(int x)
{
	int tempfa = last;
	int temp = tot;
	tot++;
	tree[temp].maxx = tree[last].maxx + 1;
	last = temp;
	while (tempfa&&tree[tempfa].son[x] == 0)
	{
		tree[tempfa].son[x] = tot - 1;
		tempfa = tree[tempfa].fa;
	}
	if (!tempfa&&tree[tempfa].son[x]==0)
	{
		tree[tempfa].son[x] = tot - 1;
		tree[last].fa = tempfa;
		ans += cal(last);
		return;
	}
	else
	{
		if (tree[tree[tempfa].son[x]].maxx - 1 == tree[tempfa].maxx)
		{
			tree[last].fa = tree[tempfa].son[x];
			ans += cal(last);
			return;
		}
		else
		{
			int now = tree[tempfa].son[x];
			tree[tot] = tree[tree[tempfa].son[x]];
			tree[tot].maxx = tree[tempfa].maxx + 1;
			ans += cal(tot) - cal(tree[tempfa].son[x]);
			tree[tree[tempfa].son[x]].fa = tot;
			tree[last].fa = tot;
			ans += cal(tree[tempfa].son[x]) + cal(last);
			while (tree[tempfa].son[x] == now)
			{
				tree[tempfa].son[x] = tot;
				if (tempfa == 0)
					break;
				tempfa = tree[tempfa].fa;
			}
			tot++;
		}

	}
}
int main()
{
	int n;
	scanf("%d", &n);
	while (n--)
	{
		int a;
		scanf("%d", &a);
		insert(a);
		printf("%lld\n", ans);
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值