zoukankan      html  css  js  c++  java
  • ●SPOJ 7258 Lexicographical Substring Search

    题链:

    http://www.spoj.com/problems/SUBLEX/
    题解:

    后缀自动机。
    首先,因为相同的子串都被存在了自动机的同一个状态里面,所以这就很自然的避免了重复子串的问题。
    然后考虑自动机里面的转移trans,发现其构成了一个DAG,且从一个状态出发,DFS下去就可以得到所有的不重复的串;
    所以我们按照拓扑序对状态排序,然后DP计算出从每个状态出发可以到达多少个子串。
    转移方程:$dp[p]=sum_{trans(p,*)=q,q!=0}dp[q]+1$
    然后对于每个输入的k,在自动机里面配合dp数组去dfs就好。

    代码:

    #include<bits/stdc++.h>
    #define MAXN 90005
    #define ll long long
    using namespace std;
    ll cnt[MAXN*3];
    struct SAM{
    	int size,last;
    	int maxs[MAXN*3],trans[MAXN*3][26],parent[MAXN*3];
    	int Newnode(int a,int b){
    		++size; maxs[size]=a;
    		memcpy(trans[size],trans[b],sizeof(trans[b]));
    		return size;
    	}
    	void Extend(int x){
    		static int p,np,q,nq;
    		p=last; last=np=Newnode(maxs[p]+1,0);
    		for(;p&&!trans[p][x];p=parent[p]) trans[p][x]=np;
    		if(!p) parent[np]=1;
    		else{
    			q=trans[p][x];
    			if(maxs[p]+1!=maxs[q]){
    				nq=Newnode(maxs[p]+1,q);
    				parent[nq]=parent[q];
    				parent[q]=parent[np]=nq;
    				for(;p&&trans[p][x]==q;p=parent[p]) trans[p][x]=nq;
    			}
    			else parent[np]=q;
    		}
    	}
    	void Build(char *S){
    		memset(trans[0],0,sizeof(trans[0]));
    		size=0; last=Newnode(0,0);
    		for(int i=0;S[i];i++) Extend(S[i]-'a');
    	}
    }SUF;
    void DP(){
    	static int in[MAXN*3],A[MAXN*3],ant,q;
    	queue<int>Q;
    	for(int p=1;p<=SUF.size;p++)
    		for(int c=0;c<26;c++){
    			q=SUF.trans[p][c]; if(!q) continue;
    			in[q]++;
    		}
    	Q.push(1);
    	while(!Q.empty()){
    		int p=Q.front(); Q.pop(); A[++ant]=p;
    		for(int c=0;c<26;c++){
    			q=SUF.trans[p][c]; if(!q) continue;
    			in[q]--; if(!in[q]) Q.push(q);
    		}
    	}
    	for(int i=ant,p;i;i--){
    		p=A[i]; cnt[p]=(p==1?0:1);
    		for(int c=0;c<26;c++){
    			q=SUF.trans[p][c]; if(!q) continue;
    			cnt[p]+=cnt[q];
    		}
    	}
    //	printf("%lld
    ",cnt[1]);
    }
    void dfs(int p,int k,char from){
    	static int i,q;
    	static char ans[MAXN];
    	if(p==1) i=0; else k--,ans[i++]=from;
    	if(!k) return (void)(ans[i]=0,puts(ans));
    	for(int c=0;c<26;c++){
    		q=SUF.trans[p][c]; if(!q) continue;
    		if(k<=cnt[q]){dfs(q,k,'a'+c); break;}
    		k-=cnt[q];
    	}
    }
    int main(){
    	static char S[MAXN];
    	scanf("%s",S);
    	SUF.Build(S);
    	DP();
    	int Q,K; scanf("%d",&Q);
    	while(Q--){
    		scanf("%d",&K);
    		dfs(1,K,0);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    位运算大集合
    一道百度面试题——位运算
    一道Google面试题——基数排序思想
    ubuntu12.04 安装nginx+php+mysql (lnmp)的web服务器环境
    Linux常用命令及Vim使用
    Gcc手册(转)
    C语言标准
    匈牙利游戏(次短路问题)
    高级打字机
    矩阵乘法
  • 原文地址:https://www.cnblogs.com/zj75211/p/8541804.html
Copyright © 2011-2022 走看看