●SPOJ 8222 NSUBSTR - Substrings

题链:

http://www.spoj.com/problems/NSUBSTR/

题解:

后缀自动机的水好深啊!
懂不了相关证明,带着结论把这个题做了。
看来这滩深水要以后再来了。

本题要用到一个叫 Right[P] 的数组,
表示 P对应的子串在原串中出现的所有位置的末尾位置下标的集合。
本题中,用这个数组存储集合大小就好了,即 P对应的子串在原串中出现了Right[p]次。

而Right[P]的值,等于从改点出发到结束状态的方案数。
但这个不好求,而是要用到另一个求法:用 Parent树:
(暂时由结论可知)Right集合不相交,只存在并列和包含关系。
所以把原来的 pre[]的指向反向后,形成一棵树,那么父节点的Right值=sigma(子节点节点的Right)
又因为这个Parent树无法遍历,且注意到儿子节点的step一定大于父亲节点的step,
所以对所有节点桶排序后,反向枚举,去更新父亲就好了。

计算出了Right[P]后,答案就可以统计了,详见代码。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 500100
#define filein(x) freopen(#x".in","r",stdin);
#define fileout(x) freopen(#x".out","w",stdout);
using namespace std;
struct SAM{
	int size,last,q,p,nq,np,len;
	int pre[MAXN],step[MAXN],Right[MAXN],ch[MAXN][26];
	int Newnode(int a,int b){
		step[size]=a; memcpy(ch[size],ch[b],sizeof(ch[b]));
		return size++;
	}
	void Extend(int x){
		p=last; last=np=Newnode(step[p]+1,0);
		while(p&&!ch[p][x]) ch[p][x]=np,p=pre[p];
		if(!p) pre[np]=1;
		else{
			q=ch[p][x];
			if(step[q]!=step[p]+1){
				nq=Newnode(step[p]+1,q);
				pre[nq]=pre[q]; pre[q]=pre[np]=nq;
				while(p&&ch[p][x]==q) ch[p][x]=nq,p=pre[p];
			}
			else pre[np]=q;
		}
	}
	void Make_Right(char *S){
		static int c[MAXN],T[MAXN]; p=1;
		for(int i=0;i<len;i++) p=ch[p][S[i]-‘a‘],Right[p]++;

		memset(c,0,sizeof(c));
		for(int i=1;i<size;i++) c[step[i]]++;
		for(int i=1;i<=len;i++) c[i]+=c[i-1];
		for(int i=1;i<size;i++) T[c[step[i]]--]=i;

		for(int i=size-1;i;i--) Right[pre[T[i]]]+=Right[T[i]];
	}
	void Build(char *S){
		len=strlen(S); memset(ch[0],0,sizeof(ch[0]));
		size=1; last=Newnode(0,0); pre[last]=0;
		for(int i=0;i<len;i++) Extend(S[i]-‘a‘);
		Make_Right(S);
	}
}suf;
int ANS[MAXN];
char S[MAXN];
int main()
{
	scanf("%s",S);
	int len=strlen(S);
	suf.Build(S);

	for(int i=1;i<suf.size;i++) ANS[suf.step[i]]=max(ANS[suf.step[i]],suf.Right[i]);
	for(int i=len-1;i;i--) ANS[i]=max(ANS[i],ANS[i+1]);
	for(int i=1;i<=len;i++) printf("%d\n",ANS[i]);
	return 0;
}
时间: 2024-08-27 15:04:00

●SPOJ 8222 NSUBSTR - Substrings的相关文章

SPOJ 8222 NSUBSTR Substrings

SAM的简单应用.... 由SAM可知从root到达的每个节点所经过的路径都对着应原串的一个子串,每个节点能到几次接收态就等于这个子串出现了几次.从最后一个节点往上走,就可以用DP更新出每个子串出现了多少次. 出现了5次的子串一定也出现了4,3,2,1次...所以最后再用长度长的给长度小的更新一下.... Substrings Time Limit: 1000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu [Submit]  

SPOJ 8222 NSUBSTR(SAM)

这几天看了N多论文研究了下后缀自己主动机.刚開始蛋疼的看着极短的代码和clj的论文硬是看不懂,后来结合其它几篇论文研究了下.总算是明确了一些 推荐文章http://blog.sina.com.cn/s/blog_70811e1a01014dkz.html 看了几篇文章认为还是这篇写的清晰明了,建议看几遍明确怎样建SAM再看了clj的论文. clj的论文中对性质的研究比較深入 以下是clj论文里推荐的一题,题意:给一个字符串S,令F(x)表示S的全部长度为x的子串中,出现次数的最大值.求F(1).

SPOJ NSUBSTR - Substrings

NSUBSTR - Substrings no tags You are given a string S which consists of 250000 lowercase latin letters at most. We define F(x) as the maximal number of times that some string with length x appears in S. For example for string 'ababa' F(3) will be 2 b

SPOJ - DISUBSTR Distinct Substrings (不相同的子串的个数)

Distinct Substrings  Time Limit: 159MS   Memory Limit: 1572864KB   64bit IO Format: %lld & %llu Description 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

后缀自动机小结 (spoj 8222)

后缀自动机理解关键点: 1. 根到任意一个结点都可以形成S的一个子串,并且S的所有子串都可以通过这种方式形成; 2. 到达该节点是所有路径就是一个right集合,一个拥有相同后缀的right集合; 3. 设某子串为str,这后缀自动机读入str后能到达的状态为right(str),即str在S中出现的位置的集合; 4. 假设node[b].fa = a,则状态a可以代替状态b进行识别. 附图: 更详细的资料: http://wenku.baidu.com/view/90f22eec551810a

【SPOJ】Distinct Substrings(后缀自动机)

[SPOJ]Distinct Substrings(后缀自动机) 题面 Vjudge 题意:求一个串的不同子串的数量 题解 对于这个串构建后缀自动机之后 我们知道每个串出现的次数就是\(right/endpos\)集合的大小 但是实际上我们没有任何必要减去不合法的数量 我们只需要累加每个节点表示的合法子串的数量即可 这个值等于\(longest-shortest+1=longest-parent.longest\) #include<iostream> #include<cstdio&g

SPOJ 8222 Substrings(后缀自动机)

[题目链接] http://www.spoj.com/problems/NSUBSTR/ [题目大意] 给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值. 求出所有的F. [题解] 在SAM中,一个串出现的次数就是|Right(s)|,我们按长度从小到大分配内存单位, 从后往前计算可以获得Right值大小,用所有的Right去更新相应长度的答案即可. [代码] #include <cstdio> #include <cstring> #include <

并不对劲的[spoj nsubstr]substrings

题意是求一个字符串每个长度的子串出现次数最多的那个出现了多少次,也就是求每个到根的最长路的right集合最大值 . 先建后缀自动机,然后将每个前缀所在的集合的初值设为1,因为所有前缀的right集合肯定不相同,而且它们包含了所有位置. 接下来按到根的最长距离从大到小排序,将right集合累加到parent上.这么排序是因为到根的最长距离长的状态肯定不是到根的最长距离短的状态的parent. 最后直接求到根的不同的最长距离的最大的right集合就行. #include<iostream> #in

spoj 8222

8222. Substrings Problem code: NSUBSTR You are given a string S which consists of 250000 lowercase latin letters at most. We define F(x) as the maximal number of times that some string with length x appears in S. For example for string 'ababa' F(3) w