题意:给你最多100个字符串,求最长的且是一半以上的字符串的公共子串,如果有多个,按字典序输出。

思路:先把各个串拼起来,中间加上一个之前未出现过的字符,然后求后缀。然后根据h数组和sa数组,求出最长的公共串。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using std::sort;
#define V 220000
int r[V],sa[V],h[V],a[V],b[V],X[V],Y[V];
int acl[120],len[110],tot,mark[V],mark_len,be[V],m[110],max_len;
char s[V],out1[V],out2[V];
void calh(int n)
{
int i,j,k=0;
for(i=1; i<=n; i++)r[sa[i]]=i;
for(i=0; i<n; h[r[i++]]=k)
for(k? k-- :0,j=sa[r[i]-1];a[i+k]==a[j+k];k++);
}
bool cmp(int *r,int a,int b,int le)
{
return (r[a]==r[b]&&r[a+le]==r[b+le]);
}
void suffix(int n,int m=128)
{
int i,j,*x=X,*y=Y,*t,p;
for(i=0;i<m;i++)b[i]=0;
for(i=0;i<n;i++)b[x[i]=a[i]]++;
for(i=1;i<m;i++)b[i]+=b[i-1];
for(i=n-1;i>=0;i--)sa[--b[x[i]]]=i;
for(j=1,p=1;p<n;m=p,j<<=1)
{
p=0;
for(i=n-j;i<n;i++)y[p++]=i;
for(i=0; i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
for(i=0; i<m;i++)b[i]=0;
for(i=0; i<n;i++)b[x[y[i]]]++;
for(i=1; i<m;i++)b[i]+=b[i-1];
for(i=n-1;i>=0;i--)sa[--b[x[y[i]]]]=y[i];
for(t=x,x=y,y=t,x[sa[0]]=0,i=1,p=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
calh(n-1);
}
void judge(int n)//先预处理每个sa属于哪个串
{
int i,j,k;
for(i=1;i<=n;i++)
{
for(j=1;j<=tot;j++)
{
if(sa[i]==len[j]){
be[i]=0;
break;
}
if(sa[i]<len[j]){
be[i]=j;
break;
}
}
}
}
int mm[110];
int getmin(int s)//排序得到当前最长的公共串长度
{
for(int i=1;i<=tot;i++)mm[i]=m[i];
sort(mm+1,mm+1+tot);
return mm[tot+1-s];
}
void solve(int n)
{
int cou=tot/2+1,cur=0,cur_len=0,i,j;//cou表示至少需要的串的数量,cur表示目前的子串所在的串的数量,cur_len表示目前子串的长度
judge(n);
memset(m,0,sizeof(m));//m表示各个串包含的子串的长度
for(i=1;i<=n;i++)
{
for(j=1;j<=tot;j++)if(m[j]>h[i])//更新mi
m[j]=h[i];
if(h[i]<max_len){
cur=0;continue;
}
if(h[i]>m[be[i]]){
if(m[be[i]]==0||m[be[i]]<max_len)cur++;
m[be[i]]=h[i];
}
if(h[i]>m[be[i-1]]){
if(m[be[i-1]]==0||m[be[i-1]]<max_len)cur++;
m[be[i-1]]=h[i];
}
if(cur>=cou){
cur_len=getmin(cou);
if(cur_len>max_len)
{
mark_len=1;
mark[0]=sa[i];
max_len=cur_len;
}
else if(cur_len==max_len)
mark[mark_len++]=sa[i];
}
}
}
int main()
{
int i,j,k,t,n;
for(i=1;i<=96;i++)acl[i]=i;
for(i=97;i<=110;i++)acl[i]=i+26;
while(scanf("%d",&t)!=-1&&t)
{
len[0]=-1;
for(i=1;i<=t;i++)
{
scanf("%s",s+len[i-1]+1);
len[i]=strlen(s);
s[len[i]]=acl[i];
}
s[len[t]]=0;
n=strlen(s);
for(i=0;i<n;i++)a[i]=s[i];
a[n]=0;
suffix(n+1);
mark_len=0;max_len=0;tot=t;
solve(n);
if(max_len==0)
printf("?\n\n");
else{
strcpy(out1,s+mark[0]);
out1[max_len]=0;
puts(out1);
for(i=1;i<mark_len;i++){
strcpy(out2,s+mark[i]);
out2[max_len]=0;
if(strcmp(out1,out2)==0)continue;
strcpy(out1,out2);
puts(out1);
}
puts("");
}
}
return 0;
}

poj 3294 Life Forms(后缀数组)的更多相关文章

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

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

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

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

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

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

  4. POJ 3294 Life Forms(后缀数组+二分答案)

    [题目链接] http://poj.org/problem?id=3294 [题目大意] 求出在至少在一半字符串中出现的最长子串. 如果有多个符合的答案,请按照字典序输出. [题解] 将所有的字符串通 ...

  5. POJ 3294 Life Forms [最长公共子串加强版 后缀数组 && 二分]

    题目:http://poj.org/problem?id=3294 Life Forms Time Limit: 5000MS   Memory Limit: 65536K Total Submiss ...

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

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

  7. POJ 1743 Musical Theme 后缀数组 最长重复不相交子串

    Musical ThemeTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://poj.org/problem?id=1743 Description ...

  8. POJ 1226 Substrings(后缀数组+二分答案)

    [题目链接] http://poj.org/problem?id=1226 [题目大意] 求在每个给出字符串中出现的最长子串的长度,字符串在出现的时候可以是倒置的. [题解] 我们将每个字符串倒置,用 ...

  9. POJ 3080 Blue Jeans 后缀数组, 高度数组 难度:1

    题目 http://poj.org/problem?id=3080 题意 有m个(2<=m<=10)不包含空格的字符串,长度为60个字符,求所有字符串中都出现过的最长公共子序列,若该子序列 ...

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

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

随机推荐

  1. cmd 窗口的复制粘贴

    如下几种方法1.点击鼠标右键,选择标志,再点击左键拖动选择要复制的内容,然后回车即可复制被 选择的内容 2.点击鼠标右键,选择标志,再点击左键拖动选择要复制的内容,然后点击鼠标右键, 此时就把选择的内 ...

  2. cmd 进入不同的驱动盘及上下级目录

    “开始”=>“运行”,输入”cmd“,此时进入的是系统管理员的等待命令 如果想进入相应的盘符,如 d 盘,则输入 cd d:\,然后再次输入 d: 即可进入 d: 盘,输入两次相当于第二在是询问 ...

  3. jquery技巧(持续更新。。)

    (1)集合处理功能         //为索引为0,1,2的元素分别设置不同的字体颜色         $('p').each(function(i){               this.styl ...

  4. Python Tutorial学习(十一)-- Brief Tour of the Standard Library – Part II

    11.1. Output Formatting 格式化输出 The repr module provides a version of repr() customized for abbreviate ...

  5. Python: 使用zipfile+io模块在内存中进行zip操作

    #!/usr/bin/env python #coding=utf-8 ''' 版权所有 (c) 2014 yao_yu (http://blog.csdn.net/yao_yu_126) 本代码采用 ...

  6. AlertDialog中EditText不能获取焦点以及不宽度不能自动铺满的完美解决方案

    问题分析: 因为 dialog的Attributes使用的默认的,其中一个属性就是:flags ,就是这个属性导致不能获取焦点,默认的是FLAG_NOT_FOCUSABLE,故名思义不能获取输入焦点, ...

  7. golang-mongodb范例

    package main import ( "log" "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" ) ...

  8. observeMode

    <!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>& ...

  9. 用C++实现绘制标尺的方法,使用了递归

    在这个例子当中将使用递归来实现一个打印标尺刻度的方法.首先是递归,函数调用其本身就叫递归,在需要将一项工作不断分为两项较小的.类似的工作时,递归非常有用,递归的方法被称为分而治之策略. 下面是一个wi ...

  10. bzoj 2331: [SCOI2011]地板 插头DP

    2331: [SCOI2011]地板 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 541  Solved: 239[Submit][Status] D ...