码迷,mamicode.com
首页 > 其他好文 > 详细

【POJ3415】Common Substrings 后缀自动机

时间:2015-01-14 15:35:14      阅读:209      评论:0      收藏:0      [点我收藏+]

标签:poj3415   common substrings   后缀自动机   sam   

转载请注明出处:http://blog.csdn.net/vmurder/article/details/42710069
其实我就是觉得原创的访问量比未授权盗版多有点不爽233。。。

题意:

给两个串,问有多少长度大于等于K的公共子串(位置不同也算一对)

题解:

后缀自动机DP

对第一个串建立后缀自动机,然后做一些预处理,

然后拿第二个串在后缀自动机上跑,到每个节点加一次贡献。

但是这样需要每个点往parent树上跑一遍,会TLE,所以可以加个lazy。

然后代码中有两次运用到拓扑序来从子向父推DP值。

呃,说得乱糟糟的。


Orz No_stop

不妨来看看他的博客:http://blog.csdn.net/no__stop/article/details/11830521


代码:

<span style="font-family:KaiTi_GB2312;font-size:18px;">#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 101000
#define T 52
using namespace std;

int pa[N<<1],son[N<<1][T],dep[N<<1];
int cnt,last;
char Sa[N],Sb[N];
int sa[N],sb[N],lena,lenb;

inline int new_node(int step){dep[++cnt]=step;return cnt;}
inline void SAM(int alp) // 把A串建立后缀自动机
{
	int p=new_node(dep[last]+1);
	int u=last;
	while(u&&!son[u][alp])son[u][alp]=p,u=pa[u];
	if(!u)pa[p]=1;
	else {
		int v=son[u][alp];
		if(dep[v]==dep[u]+1)pa[p]=v;
		else {
			int nv=new_node(dep[u]+1);
			memcpy(son[nv],son[v],sizeof son[nv]);
			pa[nv]=pa[v],pa[v]=pa[p]=nv;
			while(u&&son[u][alp]==v)son[u][alp]=nv,u=pa[u];
		}
	}
	last=p;
}

int ts[N<<1],pos[N<<1],cont[N<<1],flag[N<<1];
void Sort(){ // 基数排序使得SAM的节点在pos内呈拓扑序
	int i;
	for(i=1;i<=cnt;i++)ts[i]=0;
	for(i=1;i<=cnt;i++)ts[dep[i]]++;
	for(i=1;i<=cnt;i++)ts[i]+=ts[i-1];
	for(i=1;i<=cnt;i++)pos[ts[dep[i]]--]=i;
}

void Cal(){ // 计算从一个节点的Right集合元素数目
	int p=1,i;
	for(i=0;i<lena;i++){
		int alp=sa[i];
		cont[son[p][alp]]++;
		p=son[p][alp];
	}
	for(i=cnt;i>=1;i--){ // topo向上推
		int q=pos[i];
		cont[pa[q]]+=cont[q];
	}
}

long long ans;
int limit;
void solve(){ //在A串的SAM上跑B串
	int i;
	int temp=0,p=1;
	ans=0;
	for(i=0;i<lenb;i++){
		int d=sb[i];
		if(son[p][d]){ // temp表示当前串最长长度
			temp++;
			p=son[p][d];
		}
		else{// 失配
			while(p&&!son[p][d])p=pa[p];
			if(!p)temp=0,p=1; // 彻底失配
			else temp=dep[p]+1,p=son[p][d]; // 只退一部分
		}
		int q=p;
		if(temp>=limit){ // 匹配部分足够长
			ans+=(long long)(temp-max(limit,dep[pa[p]]+1)+1)*cont[p];
			if(limit<=dep[pa[p]])flag[pa[p]]++;
		}
		// 没有else。。显然不够就没用
	}
	for(i=cnt;i>=1;i--){// 往回推标记
		p=pos[i];
		ans+=(long long)flag[p]*(dep[p]-max(limit,dep[pa[p]]+1)+1)*cont[p];
		if(limit<=dep[pa[p]])flag[pa[p]]+=flag[p];
	}
}

void init(){
	memset(flag,0,sizeof flag);
	memset(cont,0,sizeof cont);
	memset(son,0,sizeof son);
	memset(pa,0,sizeof pa);
	last=cnt=1;
}

int main()
{
//	freopen("test.in","r",stdin);
	while(scanf("%d",&limit),limit)
	{
		init();
		scanf("%s%s",Sa,Sb);
		lena=strlen(Sa),lenb=strlen(Sb);
		for(int i=0;i<lena;i++)
		{
			if('a'<=Sa[i]&&Sa[i]<='z')sa[i]=Sa[i]-'a';
			else sa[i]=Sa[i]-'A'+26;
			SAM(sa[i]);
		}
		for(int i=0;i<lenb;i++)
		{
			if('a'<=Sb[i]&&Sb[i]<='z')sb[i]=Sb[i]-'a';
			else sb[i]=Sb[i]-'A'+26;
		}
		Sort();
		Cal();
		solve();
		cout<<ans<<endl;
	}
}
</span>


【POJ3415】Common Substrings 后缀自动机

标签:poj3415   common substrings   后缀自动机   sam   

原文地址:http://blog.csdn.net/vmurder/article/details/42710069

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!