Jzoj4876 基因突变(红警系列)

邪恶的707刚刚从白垩纪穿越回来,心中产生了一个念头:我要统治人类!
    但是统治人类是很庞大且复杂的一个工程,707尝试了洗脑,催眠,以及武装镇压都没能成功地统治人类,于是她决定从科学上对人类的基因进行研究从而达到他的目的。
    707获取了人类的基因信息并尝试对基因进行实验。他发现可以把人类的基因看做一个只包含小写字母的字符串,并定义从头开始任意长度的基因为“源头基因”人类身上与源头基因完全匹配的片段越多,这个人就越容易被控制。于是707就开始了他邪恶的计划……

    作为人类卫士的射手ZMiG自然不会让707得逞,他决定拯救人类,现在他拿到了其中一个人被改造后的基因,他想请你统计一下它的基因中究竟有多少基因片段是可以与源头基因相匹配的

是这样的,我们要做的事情就是拿着原串和原串做扩展KMP

然而扩展KMP早就忘得一干二净

这种题目,显然是可以用SAM做的,跑出sam和parent树,让后直接在自动机上跑原串累加size就是答案(未完!下面还有)

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 1000010
using namespace std;
char str[N];
int s[N][26],mx[N],sz[N],f[N];
int n,cnt=1,lst=1,ans=0,v[N],r[N];
inline int extend(char c){
	int p=lst,np=lst=++cnt,q,nq;
	c-='a'; mx[np]=mx[p]+1; sz[np]=1; 
	for(;p&&!s[p][c];p=f[p]) s[p][c]=np;
	if(!p) return f[np]=1;
	q=s[p][c];
	if(mx[p]+1==mx[q]) f[np]=q;
	else {
		nq=++cnt;
		mx[nq]=mx[p]+1;
		f[nq]=f[q]; f[q]=f[np]=nq;
		memcpy(s[nq],s[q],26<<2);
		for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq;
	}
}
int main(){
	freopen("gene.in","r",stdin);
	freopen("gene.out","w",stdout);
	scanf("%s",str+1); n=strlen(str+1);
	for(int i=1;i<=n;++i) extend(str[i]);
	for(int i=1;i<=cnt;++i) ++v[mx[i]];
	for(int i=1;i<=n;++i) v[i]+=v[i-1];
	for(int i=cnt;i;--i) r[v[mx[i]]--]=i;
	for(int i=cnt;i;--i) sz[f[r[i]]]+=sz[r[i]];
	for(int i=1,x=1;i<=n;++i){
		x=s[x][str[i]-'a']; ans+=sz[x];
	}	
	printf("%d\n",ans);
}
但是问题就是: Memory Limits: 131072 KB

这就十分尴尬了

于是不得不换一种方法

我们有hash!(下面还有)

#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 1000010
#define L long long
using namespace std;
char s[N]; int n;
L ans=0,b[N]={1},h[N];
inline L gH(int l,int r){ return h[r]-h[l-1]*b[r-l+1]; }
int dijk(int x){
	int l=-1,r=n-x;
	for(int m;l<r;){
		m=l+r+1>>1;
		if(gH(1,m+1)==gH(x,x+m)) l=m;
			else r=m-1;
	}
	return l+1;
}
int main(){
	freopen("gene.in","r",stdin);
	freopen("gene.out","w",stdout);
	scanf("%s",s+1); n=strlen(s+1);
	for(int i=1;i<=n;++i){
		b[i]=b[i-1]*27;
		h[i]=h[i-1]*27+s[i]-'a';
	}
	for(int i=1;i<=n;++i) ans+=dijk(i);
	printf("%lld\n",ans);
}
这样可以卡过去了但是效率比较低啊

正解:动态规划

我们跑出kmp的next数组,计f[i]表示以i结尾的,符合条件的子串个数

转移十分显然:f[i]=f[next[i]]+1

答案:Σf[i]

优美而简洁

#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
char s[1000010]; long long ans=0;
int n,nt[1000010],f[1000010];
int main(){
	freopen("gene.in","r",stdin);
	freopen("gene.out","w",stdout);
	scanf("%s",s); n=strlen(s);
	for(int i=1,j;i<n;++i)
		for(j=i;j>0;) 
			if(s[j=nt[j]]==s[i]){ nt[i+1]=j+1; break; }
	for(int i=1;i<=n;++i) f[i]=f[nt[i]]+1,ans+=f[i];
	printf("%lld\n",ans);
}

posted @ 2017-10-31 08:10  扩展的灰(Extended_Ash)  阅读(367)  评论(0编辑  收藏  举报