zoukankan      html  css  js  c++  java
  • ●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
    ",ANS[i]);
    	return 0;
    }
    

  • 相关阅读:
    大端与小端编号方法的区别
    socket、listen 等函数的打电话隐喻
    windows 网络编程报错 error LNK2019
    有符号数与无符号数之间的转换
    C++ 代码命名建议
    编写启发式代码的方法
    求给定数目的前 n 个素数
    不使用 “+” 实现加法操作
    二叉搜索树中两个节点的旋转
    Python玩转硬件:TPYBoard-Micropython开发板大盘点
  • 原文地址:https://www.cnblogs.com/zj75211/p/7988556.html
Copyright © 2011-2022 走看看