P4051 [JSOI2007]字符加密 SAM

我承认我是个傻逼

也只有我这么离谱会用(SAM)来求(SA)

对这道题来说,还是比较简单的,只需要复制原串一遍贴到后面,然后求一遍(SA)即可


接下来我给诸位介绍一下我研究了两个下午,然后(MLE)的成果


首先我们对字符串建出(SAM),考虑S(AM)的本质是一个(DAG)图和一棵后缀树

因为我们要对后缀进行排序,所以我们要尽可能的利用后缀树来解决问题

很明显在后缀树上按字典序遍历即可得到(SA)

所以我们现在的问题就是如何解决如何按字典序遍历

求出其父节点与自身加长的串中第一个字符大小,用它来排序

然后加边(dfs)即可

算了我来给你们翻译一下(solve)函数

inline void solve()
{
	for(int i=1;i<=cnt;i++) link[i] = ge(c[n + 1 - Right[i] + node[node[i].fa].len]) , bac[link[i]]++;//求出第一个字符
	for(int i=1;i<=256;i++) bac[i]+= bac[i-1];
	for(int i=1;i<=cnt;i++) Ti[bac[link[i]]--] = i;//相当于把所有link都丢进64个桶里,节点越大在对应的桶里越靠后
	for(int i=cnt;i>=1;i--) add(node[Ti[i]].fa , Ti[i]);//倒着加边意味着z类边优先加入
}

(Code)

#include<bits/stdc++.h>

using namespace std;

#define INF 1ll<<30
#define ill long long
#define sto set<node>::iterator

template<typename _T>
inline void read(_T &x)
{
	x=0;char s=getchar();int f=1;
	while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	x*=f;
}

int ge(char ch)
{
    if (ch<='9' && ch>='0') return ch-'0';
    if (ch<='Z' && ch>='A') return ch-'A'+10;
    if (ch<='z' && ch>='a') return ch-'a'+36;
    return ch;
}
const int np = 2e5+5;

struct SAM{
	int fa;
	int len;
	int son[80];
	int pos;
}node[np * 2];

int cnt = 1 , la = 1,n;
int Right[np * 2];
inline void insert(int x,int i)
{
	int  k,p,q,now;
	p = la,now = la = ++cnt;
	Right[now] =node[now].len = node[p].len + 1, node[now].pos = i;
	for( ;p && !node[p].son[x] ;node[p].son[x] = now , p = node[p].fa );
	if(!p) return (void)(node[now].fa = 1);
	if(node[p].len + 1 == node[q = node[p].son[x] ].len) return (void)(node[now].fa = q);
	node[k = ++cnt].len = node[p].len + 1 , node[k].fa = node[q].fa ; node[q].fa = node[now].fa = k,Right[k] = Right[q] ,memcpy(node[k].son , node[q].son , sizeof(node[q].son));
	for( ; p&& !(node[p].son[x] ^ q) ; node[p].son[x] = k , p = node[p].fa) ;
}

int link[np * 2] , bac[np * 2];
int Ti[np * 2],head[np * 2] , nxt[np * 2] , ver[np *2];
int tit;
char c[np * 2];

inline void add(int a,int b)
{
	ver[++tit] = b;
	nxt[tit] = head[a];
	head[a] = tit;
}

inline void solve()
{
	for(int i=1;i<=cnt;i++) link[i] = ge(c[n + 1 - Right[i] + node[node[i].fa].len]) , bac[link[i]]++;
	for(int i=1;i<=256;i++) bac[i]+= bac[i-1];
	for(int i=1;i<=cnt;i++) Ti[bac[link[i]]--] = i;
	for(int i=cnt;i>=1;i--) add(node[Ti[i]].fa , Ti[i]);
}

int sa[np * 2];
int gs = 0;

inline void dfs(int x)
{
	if(node[x].pos)	sa[++gs] = node[x].pos;
	for(int i=head[x];i;i=nxt[i])
	dfs(ver[i]);
}

signed  main()
{
	scanf("%s",c+1);
	n = strlen(c+1);
	int Len  = n;
	for(int i=1;i<=n;i++)
	c[i + n] = c[i];
	n = strlen(c + 1);
	for(int i=n;i>=1;i--) insert(ge(c[i]) , i);
	solve();
	dfs(1);
	for(int i=1;i<=gs;i++)
	{
		if(sa[i] > Len) continue;
		else
		{
			cout<<c[sa[i] + Len - 1];
		}
	}
 }

(End)

事实证明(SAM)的最大阻力就是空间太小

(10^6)以内(128MB)是解决不了问题的
(map)也没用

原文地址:https://www.cnblogs.com/-Iris-/p/15350304.html