bzoj2946 [Poi2000]公共串

原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=2946

题意:

给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:

  • 读入单词
  • 计算最长公共子串的长度
  • 输出结果

数据范围
1<=n<=5,单词长度<=2000

题解:
发现不会写SA了,回忆一发。

对于SA来说,求多个串的公共串是一个比较好处理的问题,
把所有串用不同间隔符连起来,跑后缀数组。
二分长度,对height分组,然后看一个组内是不是包含所有的串。

如果用SAM处理这个问题:
法一:
抄论文

将所有S_i连接在一起成为一个新的字符串T,其中每个S_i后要加上一个不同的分隔符D_i(即加上K个额外的不同特殊字符D_1~D_K):
我们对字符串T构建后缀自动机。
现在我们需要在后缀自动机找出一个字符串,它是所有字符串S_i的子串。注意到如果一个子串在某个字符串S_j中出现过,那么后缀自动机中存在一
条以这个子串为前缀的路径,包含分隔符D_j,但不包含其他分隔符D_1,…,D_j-1,D_j+1,…,D_k。
因此,我们需要计算“可达性”:对自动机中的每个状态和每个字符D_i,计算是否有一条从该状态开始的路径,包含分隔符D_i,但不包含别的分隔符。
很容易用DFS/BFS或者动态规划实现。在此之后,原问题的答案就是字符串longest(v),其中v能够到达所有的分隔符。

法二:
对第一个串建SAM,然后其他的串在上面跑,失配跳parent,得到每个状态匹配每个串的最长匹配长度。
然后每个状态的每个串取最小值,每个状态取最大值。
这里有一个要注意的地方,正如AC自动机的节点需要更新到fail一样,
在SAM上也可能出现这个串走了这个节点,另一个串走到它的pa,而没有更新到的情况,
所以还要按拓扑序把它的答案更新到它的pa上。
这样不会多算,因为本身由于是第一个串建的SAM,一个状态i的最大值也不过是len[i]。

SA代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100010;
const int M=2005;
int a[N],b[N],w[N],sa[N],rk[N],ht[N],s[N],pos[N];
bool vis[6];
char str[6][M];
void getsa(int n)
{int *x=a; int *y=b; int m=127;//x是rank,rank有重for(int i=0;i<=n;i++) w[x[i]=s[i]]++;for(int i=1;i<=127;i++) w[i]+=w[i-1];for(int i=n;i>=0;i--) sa[--w[x[i]]]=i;for(int i=0;i<=127;i++) w[i]=0;for(int l=1,p=0;l<=n;l=l<<1) //y为第二关键字sa{for(int i=n-l+1;i<=n;i++) y[p++]=i;for(int i=0;i<=n;i++) if(sa[i]-l>=0) y[p++]=sa[i]-l;//然后求新safor(int i=0;i<=n;i++) w[x[i]]++;for(int i=1;i<=m;i++) w[i]+=w[i-1];for(int i=n;i>=0;i--) sa[--w[x[y[i]]]]=y[i];for(int i=0;i<=m;i++) w[i]=0;//然后求新rankswap(x,y); //现在y是旧rank,sa是新sa,求新rankp=1; x[sa[0]]=0;for(int i=1;i<=n;i++) x[sa[i]]= (y[sa[i]]==y[sa[i-1]]&&y[sa[i]+l]==y[sa[i-1]+l])?p-1:p++;m=p; p=0;if(p>n) break;}
}
void getheight(int n)
{int k=0; for(int i=0;i<=n;i++) rk[sa[i]]=i;ht[rk[0]]=0; for(int i=0;i<n;i++){if(k) k--;int j=sa[rk[i]-1];while(s[i+k]==s[j+k]) k++;ht[rk[i]]=k;}
}
bool check(int k,int n,int m)
{memset(vis,0,sizeof(vis));for(int i=2;i<=n;i++){if(ht[i]>=k) vis[pos[sa[i]]]=1;else{bool flag=0; for(int j=1;j<=m;j++) {if(!vis[j]) flag=1; vis[j]=0;}if(!flag) return 1;vis[pos[sa[i]]]=1;}}bool flag=0;for(int i=1;i<=m;i++) {if(!vis[i]) flag=1; vis[i]=0;}if(!flag) return 1;return 0;
}
int main()
{int n; int len=0;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%s",str[i]); int l=strlen(str[i]);for(int j=0;j<l;j++) {pos[len]=i; s[len++]=str[i][j];}if(i!=n) s[len++]=i;}getsa(len);getheight(len);int lf=0; int rg=len;while(lf+1<rg){int mid=(lf+rg)>>1;if(check(mid,len,n)) lf=mid;else rg=mid;}if(check(rg,len,n)) printf("%d\n",rg);else printf("%d\n",lf);return 0;
}

SAM代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=10005;
char s[N];
struct node
{int ch[26],pa;
}tr[N];
int len[N],last=1,tail=1,root=1,n,ans[N],ret[N],q[N],w[N];
void insert(int c)
{int nd=++tail; len[nd]=len[last]+1; int tmp=last;for(;tmp&&!tr[tmp].ch[c];tmp=tr[tmp].pa) tr[tmp].ch[c]=nd;if(!tmp) tr[nd].pa=root;else{int B=tr[tmp].ch[c];if(len[B]==len[tmp]+1) tr[nd].pa=B;else{int nB=++tail; tr[nB]=tr[B]; len[nB]=len[tmp]+1;for(;tmp&&tr[tmp].ch[c]==B;tmp=tr[tmp].pa) tr[tmp].ch[c]=nB;tr[B].pa=tr[nd].pa=nB;}}last=nd;
}
void gettopu(int L)
{for(int i=1;i<=tail;i++) ans[i]=len[i];for(int i=1;i<=tail;i++) w[len[i]]++;for(int i=1;i<=L;i++) w[i]+=w[i-1];for(int i=1;i<=tail;i++) q[w[len[i]]--]=i;
}
void getans()
{int L=strlen(s); int cur=0; int tmp=root;memset(ret,0,sizeof(ret));for(int i=0;i<L;i++){int c=s[i]-'a';while(tmp&&!tr[tmp].ch[c]) tmp=tr[tmp].pa;if(!tmp) {tmp=1; cur=0;}else {cur=min(cur,len[tmp])+1; tmp=tr[tmp].ch[c];}ret[tmp]=max(ret[tmp],cur);}for(int i=tail;i>=1;i--) ret[tr[q[i]].pa]=max(ret[q[i]],ret[tr[q[i]].pa]);for(int i=1;i<=tail;i++) ans[i]=min(ret[i],ans[i]);
}
int main()
{scanf("%d",&n);scanf("%s",s); int L=strlen(s);for(int i=0;i<L;i++) insert(s[i]-'a');gettopu(L);for(int i=1;i<n;i++) {scanf("%s",s); getans();}int answer=0; for(int i=1;i<=tail;i++) answer=max(answer,ans[i]);printf("%d\n",answer);return 0;
}

bzoj2946 [Poi2000]公共串(后缀数组 || 后缀自动机)相关推荐

  1. 【二分答案】【哈希表】【字符串哈希】bzoj2946 [Poi2000]公共串

    二分答案,然后搞出hash值扔到哈希表里.期望复杂度O(n*log(n)). <法一>next数组版哈希表 #include<cstdio> #include<cstri ...

  2. bzoj 2946 [Poi2000]公共串——后缀自动机

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2946 对每个串都建一个后缀自动机,然后 dfs 其中一个自动机,记录同步的话在别的自动机上走 ...

  3. luoguP5108 仰望半月的夜空 [官方?]题解 后缀数组 / 后缀树 / 后缀自动机 + 线段树 / st表 + 二分...

    仰望半月的夜空 题解 可以的话,支持一下原作吧... 这道题数据很弱..... 因此各种乱搞估计都是能过的.... 算法一 暴力长度然后判断判断,复杂度\(O(n^3)\) 期望得分15分 算法二 通 ...

  4. 五分钟搞懂后缀数组!

    为什么学后缀数组 后缀数组是一个比较强大的处理字符串的算法,是有关字符串的基础算法,所以必须掌握.  学会后缀自动机(SAM)就不用学后缀数组(SA)了?不,虽然SAM看起来更为强大和全面,但是有些S ...

  5. 五分钟搞懂后缀数组!后缀数组解析以及应用(附详解代码)

    为什么学后缀数组 后缀数组是一个比较强大的处理字符串的算法,是有关字符串的基础算法,所以必须掌握. 学会后缀自动机(SAM)就不用学后缀数组(SA)了?不,虽然SAM看起来更为强大和全面,但是有些SA ...

  6. sa后缀数组使用合集,包括height数组求LPC和LCS,ST表,单调队列优化。

    P5546 [POI2000]公共串 所有串合在一起,每两个串放不同的字符,求一遍后缀数组,然后利用height数组求LCS即可. #include<iostream> #include& ...

  7. 后缀数组求最长重复子串

    问题描述 给定一个字符串,求出其最长重复子串 例如:abcdabcd 最长重复子串是 abcd,最长重复子串可以重叠 例如:abcdabcda,这时最长重复子串是 abcda,中间的 a 是被重叠的. ...

  8. 后缀数组(倍增)学习记录,我尽可能详细的讲了

    后缀数组(倍增) 后缀数组 后缀数组能干什么 一些基本概念 那么到底怎么排序呢? 倍增排序 具体执行排序呢? 基数排序 关于排序的桶 关于桶排序在字符串倍增中的嵌入 具体改执行的排序事情 倍增排序的代 ...

  9. 后缀数组(学习心得)

    后缀数组 后缀数组是一种处理字符串的利器,很多字符串的问题都可以通过后缀数组来实现. 后缀数组说简单一点就是对一个字符串的所有后缀子串进行排序. 我来举个例子,比如字符串banana 刚开始的时候它的 ...

最新文章

  1. JavaEE——JavaScript
  2. I/O:OutputStream
  3. java获取月末日期_Java用于取得当前日期相对应的月初,月末,季初,季末,年初,年末时间详解...
  4. X Window概述
  5. Spring ListFactoryBean实例
  6. hdu3072 Intelligence System (最小树形图?)
  7. [react] 请说说什么是useState?为什么要使用useState?
  8. 图像目标分割_4 DeepLab-V1
  9. 字典(dict)按键(key)和值(value)排序即item()的常用方式
  10. 阿里云在西雅图招人挖角 微软居多
  11. 微信支付v2开发(6) 发货通知
  12. Spring Cloud Eureka详解
  13. 单片机3x3矩阵键盘c语言,C51单片机的3*3矩阵键盘程序
  14. Bootstrap的160个小图标和使用
  15. c语言数组输入某年某月某日,编程:输入某年某月某日,判断这一天是这一年的第几天...
  16. react-native Animated简单动画制作
  17. 【数据结构与算法】之深入解析“滑动谜题”的求解思路与算法示例
  18. 1.5.33 计算分数加减表达式的值
  19. C# 处理PPT水印(三)—— 在PPT中添加多行(平铺)文本水印效果
  20. 三种经典iPhone上网络抓包方法详解

热门文章

  1. 服务器处理文件的io瓶颈,解决服务器数据传输瓶颈的IO技术
  2. 阿里二面:Redis有几种集群方案?我答了4种
  3. 【田姓】宗谱——【郡望堂号】
  4. zuul : Forwarding error 全局异常处理
  5. 玩安卓从 0 到 1 之架构思考
  6. 5.pygame图片显示
  7. srio 门铃_如何更改SkyBell HD门铃的LED颜色
  8. 房产管理系统平台平台管理分析
  9. java 查找大写字母_在Java中查找字符串的所有大写字母
  10. 华清远见上海中心22071班--11.19作业