题意

输入n(n<=100)个字符串,每个字符串长度<=1000,你的任务是找出一个最长的字符串使得超过一半的字符串都包含这个字符串。

分析

训练指南上后缀数组的一道例题,据说很经典(估计也就是height分组比较常用)。但是训练指南上给出的中文题面真滴坑B啊!书上说,连续出现,我懵逼了好久!

我们把这n个字符串连成一个长的字符串S,且中间用不同的未出现的字符相隔开(为什么隔开我们后面说),比如样例一会变为abcdefg1bcdefgh2cdefghi3。这样每一段是一个原字符串。然后问题转换为,在S字符串中,找出一个最长的字符串,使得至少有n/2的匹配点与这个最长的字符串相匹配,且匹配点分别位于至少n/2段。

求最长的时候我们二分答案,然后判断mid是否满足条件。关键是如何判断。我们回想一下通过height数组求LCP的方法,我们知道LCP(i,j)=RMQ(height,rank[i]+1,rank[j]).这里和这个原理是类似的。我们遍历height数组,当height[i]<mid的时候,就新增一个分组。也就是每个分组内的LCP都是>=mid的。然后判断一下这个分组内的后缀是否符合上面说的那个条件(既分组内至少有n/2个后缀,且后缀分别位于至少n/2段)。如果符合则说明mid是合法的。我们上面说过,不同的原串我们在合成S的时候,之间是用特殊字符隔开的,就是为了保证这里每个LCP都是在一个段内。

当时我还在纠结过,这样只是求出最长长度,但是题目要求输出啊。其实,你只要再跑一下上面判断的过程,把那些符合条件的LCP都输出出来就可以了。

mmp宿舍又又又半夜停电还特么这么多蚊子,感觉最后被劝退ACM的不是自己的菜而是暑假集训期间宿舍的温度····

下面是ac的code

 
 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream> using namespace std;
const int maxn=+;
const int maxs=+;
char s[maxn*maxs];
int c[maxn*maxs],t[maxn*maxs],t2[maxn*maxs],sa[maxn*maxs];
int n,N;
void build_sa(int m){
int *x=t,*y=t2;
for(int i=;i<m;i++)c[i]=;
for(int i=;i<n;i++)c[x[i]=s[i]]++;
for(int i=;i<m;i++)c[i]+=c[i-];
for(int i=n-;i>=;i--)sa[--c[x[i]]]=i; for(int k=;k<=n;k<<=){
int p=;
for(int i=n-k;i<n;i++)y[p++]=i;
for(int i=;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
for(int i=;i<m;i++)c[i]=;
for(int i=;i<n;i++)c[x[y[i]]]++;//这里为啥是这样?一会结合基数排序想一下
for(int i=;i<m;i++)c[i]+=c[i-];
for(int i=n-;i>=;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=,x[sa[]]=;
for(int i=;i<n;i++)
x[sa[i]]=y[sa[i]]==y[sa[i-]]&&y[sa[i]+k]==y[sa[i-]+k]?p-:p++;
if(p>=n)break;
m=p;
}
} int Rank[maxn*maxs],height[maxn*maxs];
void getHeight(){//这里有个bug,sa[0]不能等于0
int k=;
for(int i=;i<n;i++)Rank[sa[i]]=i;
for(int i=;i<n;i++){
if(i==&&Rank[i]==)
continue;
if(k)k--;
int j=sa[Rank[i]-];
while(s[i+k]==s[j+k])k++;
height[Rank[i]]=k;
}
} char word[maxs];
int idx[maxn*maxs];
int maxlen;
void add(int len,int id){
for(int i=;i<len;i++){
idx[n]=id;
s[n++]=word[i]-'a';
}
s[n]=+id;
idx[n++]=N;
} bool good(int L,int R){
if(R-L+<=N/)
return false;
int res=;
int vis[maxn];
memset(vis,,sizeof(vis));
for(int i=L;i<=R;i++){
int id=idx[sa[i]];
if(!vis[id]&&id!=N){
vis[id]=;
res++;
}
}
if(res>N/)
return true;
return false;
} bool judge(int mid){
int L=;
for(int R=;R<n;R++){
if(height[R]<mid){
if(good(L,R-)){
return true;
}
L=R;
}
}
return false;
} void print(int mid){
int L=;
for(int R=;R<n;R++){
if(height[R]<mid){
if(good(L,R-)){
for(int i=;i<mid;i++){
printf("%c",s[i+sa[L]]+'a');
}
printf("\n");
}
L=R;
}
}
}
int main(){
int kase=;
while(scanf("%d",&N)!=EOF&&N){
if(kase)printf("\n");
kase=;
n=;
for(int i=;i<N;i++){
scanf("%s",word);
int len=strlen(word);
maxlen=max(maxlen,len);
add(len,i);
}
if(N==){
printf("%s\n",word);
continue;
}
build_sa(+N);
getHeight();
int L=,R=maxlen;
int ans=-;
while(L<=R){
int M=L+(R-L)/;
if(judge(M))
{
ans = M;
L=M+;
}
else
R=M-;
}
if(ans==-){
printf("?\n");
continue;
}
print(ans);
}
return ;
}

【UVA11107 训练指南】Life Forms【后缀数组】的更多相关文章

  1. UVA11107 Life Forms --- 后缀数组

    UVA11107 Life Forms 题目描述: 求出出现在一半以上的字符串内的最长字符串. 数据范围: \(\sum len(string) <= 10^{5}\) 非常坑的题目. 思路非常 ...

  2. POJ3294 Life Forms —— 后缀数组 最长公共子串

    题目链接:https://vjudge.net/problem/POJ-3294 Life Forms Time Limit: 5000MS   Memory Limit: 65536K Total ...

  3. Poj 3294 Life Forms (后缀数组 + 二分 + Hash)

    题目链接: Poj 3294 Life Forms 题目描述: 有n个文本串,问在一半以上的文本串出现过的最长连续子串? 解题思路: 可以把文本串用没有出现过的不同字符连起来,然后求新文本串的heig ...

  4. 2016多校联合训练4 F - Substring 后缀数组

    Description ?? is practicing his program skill, and now he is given a string, he has to calculate th ...

  5. POJ3294 Life Forms(后缀数组)

    引用罗穗骞论文中的话: 将n 个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求后缀数组.然后二分答案,用和例3 同样的方法将后缀分成若干组,判断每组的后缀是否出现在不小于k 个的原串中 ...

  6. POJ 3294 UVA 11107 Life Forms 后缀数组

    相同的题目,输出格式有区别. 给定n个字符串,求最长的子串,使得它同时出现在一半以上的串中. 不熟悉后缀数组的童鞋建议先去看一看如何用后缀数组计算两个字符串的最长公共子串 Ural1517 这道题的思 ...

  7. POJ3294--Life Forms 后缀数组+二分答案 大于k个字符串的最长公共子串

                                                                              Life Forms Time Limit: 500 ...

  8. POJ 3294 Life Forms 后缀数组+二分 求至少k个字符串中包含的最长子串

    Life Forms   Description You may have wondered why most extraterrestrial life forms resemble humans, ...

  9. poj 3294 Life Forms - 后缀数组 - 二分答案

    题目传送门 传送门I 传送门II 题目大意 给定$n$个串,询问所有出现在严格大于$\frac{n}{2}$个串的最长串.不存在输出'?' 用奇怪的字符把它们连接起来.然后求sa,hei,二分答案,按 ...

随机推荐

  1. Django之ORM数据库

    5.1 数据库的配置 1    django默认支持sqlite,mysql, oracle,postgresql数据库.  <1> sqlite django默认使用sqlite的数据库 ...

  2. python编辑excel

    转: http://www.cnblogs.com/lhj588/archive/2012/01/06/2314181.html

  3. git 提交新创建的文件

    git add -u:把所有tracked文件中被修改过或已删除文件的信息添加到索引库.它不会处理untracted的文件. 如果想将本地新创建的文件也提交上去,需执行如下操作:

  4. Linux应用开发入门(转)

    今天偶然看到这篇文章,做个入门了解还是不错的. 前一阵子在QQ上和朋友聊天的时候,总会看到有人说Linux上的应用程序开发是高手才可以完成的,而且这种“迷信”在目前似乎还很普遍.然而,情况并不是这样的 ...

  5. Devexpress VCL Build v2013 vol 14.1.3 发布

    我修,我修,修修修. New Major Features in 14.1 What's New in VCL Products 14.1 Breaking Changes To learn abou ...

  6. iCn3D结构查看器的实现方法

    iCn3D Structure Viewer:iCn3D结构查看器 演示效果如下: 上面只是一个Basic UI的演示,如果要Advanced UI的话可以去NCBI官网看API

  7. [operator]Ubuntu server 18 设置静态IP

    root@ubuntu-MesosMaster-Marathon:~# cat /etc/netplan/-cloud-init.yaml # This file is generated from ...

  8. 安装memcache服务

    d:\tools\memcache\setup\memcached -d install

  9. gj11 多线程、多进程和线程池编程

    11.1 python中的GIL # coding=utf-8 # gil global interpreter lock (cpython) # python中一个线程对应于c语言中的一个线程 # ...

  10. MapGIS Mobile开发

    1. 先将Android开发环境配置好(包括Java + Eclipse + Android SDK) 2. 加载API类库(运行MapGIS 10 AndroidSDK.exe可以加载Mobile框 ...