标签: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