后缀数组练习4:Life Forms
有一个细节不是特别懂,然后的话细节有点多,就是挺难发现的那一种,感谢大佬的博客
1470: 后缀数组4:Life Forms
时间限制: 1 Sec 内存限制: 128 MB
提交: 112 解决: 35
[提交] [状态] [讨论版] [命题人:admin]题目描述
【问题描述】
求n个字符串(长度1000)的最长的一个子串,满足该子串在一半以上(不包括一半)的字符串中出现过,并输出该子串,如果有多个子串满足要求,则按字典序输出所有的子串;(全部都是小写字母)
【输入格式】
输入N(1<=N<=100,还真有为1的数据哟)(每个测试点中数据组数不超过100)
【输出格式】
对于每个测试样例,输出答案。如果有很多,则按字典序输出。如果没有解决方案,至少有一个字母,输出“?”在测试用例之间留下一条空行。
接下来是N个字符串
(有多组数据,N为0时结束)
【样例】
输入:3
abcdefg
bcdefgh
cdefghi
3
xxx
yyy
zzz
0
输出:bcdefg
cdefgh
?
- 我们把他合并起来了,和上一题的处理方法很相似,但是的话我们的ASCII只有127,所以我们要用一个a数组来保存,才可以跑get_sa
- 因为合并了,所以一定要保证他们不是在同一个串里面,其次的话,这道题有一个比较神奇的概念,就是后缀分组,我们把后缀分成好几组,看看哪一组是满足条件的,记录答案,取最大值
(引自罗穗骞)
- 因为我们要后缀分组,所以的话要开一个bool型的v数组判断他有没有出现过,然后用一个belong数组来记录在哪一串
- 然后的话,不知道有一个就是说get_he里面如果Rank[i]==1,就要直接continue,我也不知道为什么,不然会被卡住
大概就是这些了,剩下的就
代码的实现
(注释版,就是解释了一下数组的意思)
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
int sa[],Rank[],rsort[];
int cnt[],pos[],height[];
int a[],belong[],v[];/*因为我们中间有空格,所以的话要开到至少101000*/
/*belong数组记录属于哪一串,v数组是用来判断两个子串是否分别属于未出现过的子串*/
bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];}
char s[];
void get_sa(int n,int m)
{
int k=,p=,len;
for(int i=;i<=n;i++) Rank[i]=a[i];
memset(rsort,,sizeof(rsort));
for(int i=;i<=n;i++) rsort[Rank[i]]++;
for(int i=;i<=m;i++) rsort[i]+=rsort[i-];
for(int i=n;i>=;i--) sa[rsort[Rank[i]]--]=i;
for(int k=;k<n;k<<=)
{
len=;
for(int i=n-k+;i<=n;i++) pos[++len]=i;
for(int i=;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k;
for(int i=;i<=n;i++) cnt[i]=Rank[pos[i]];
memset(rsort,,sizeof(rsort));
for(int i=;i<=n;i++) rsort[cnt[i]]++;
for(int i=;i<=m;i++) rsort[i]+=rsort[i-];
for(int i=n;i>=;i--) sa[rsort[cnt[i]]--]=pos[i];
for(int i=;i<=n;i++) cnt[i]=Rank[i];
p=; Rank[sa[]]=;
for(int i=;i<=n;i++)
{
if(!cmp(sa[i],sa[i-],k)) p++;
Rank[sa[i]]=p;
}
if(p==n) break; m=p;
}
a[]=; sa[]=;
}
void get_he(int n)
{
int j,k=;
for(int i=;i<=n;i++)
{
if(Rank[i]==) continue;/*一定要这样,但是我也不知道为什么没有这个会错*/
j=sa[Rank[i]-];
if(k) k--;
while(a[j+k]==a[i+k]) k++;
height[Rank[i]]=k;
}
}
bool check(int mid,int k,int n)/*二分搜索答案*/
{
int ans=;
memset(v,,sizeof(v)); v[]=;
if(!v[belong[sa[]]]) ans++;/*没出现过*/
v[belong[sa[]]]=;
for(int i=;i<=n;i++)
{
/*分开两边来判断,只要满足条件就是好的,其实就是分组后缀,看一下哪一组满足条件*/
if(height[i]<mid)/*在左边*/
{
memset(v,,sizeof(v)); v[]=;
ans=;/*初始化一下*/
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;/*判断*/
}
else/*在右边*/
{
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;/*判断*/
}
if(ans>=k)return true;/*满足条件*/
}
return false;
}
void putt(int x,int k,int n)/*输出答案*/
{
int ans=;
memset(v,,sizeof(v)); v[]=;
if(!v[belong[sa[]]]) ans++;/*没出现过*/
v[belong[sa[]]]=;
for(int i=;i<=n;i++)
{
if(height[i]<x)/*在左边*/
{
if(ans>=k)
{
for(int j=sa[i-];j<=sa[i-]+x-;j++) printf("%c",a[j]+'a');
printf("\n");
}/*已经满足条件了就可以直接输出*/
memset(v,,sizeof(v)); v[]=;
ans=;
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;/*判断*/
}
else/*在右边*/
{
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;
}
}
if(ans>=k)/*符合条件就输出*/
{
for(int i=sa[n];i<=sa[n]+x-;i++) printf("%c",a[i]+'a');
printf("\n");
}
}
int main()
{
int t;
while(scanf("%d",&t)!=EOF && t)
{
int n=,tp=,tt=t/+;
for(int i=;i<=t;i++)
{
scanf("%s",s+);
for(int j=;j<=strlen(s+);j++)
{
a[++n]=s[j]-'a';
belong[n]=i;/*属于哪一串*/
}
a[++n]=tp; tp++;/*把二十六个字母保存到A数组里面,ascii只有127位,所以不可以直接保存*/
}
if(t==)/*只有一个的话直接输出就好了*/
{
for(int i=;i<n;i++) printf("%c",a[i]+'a');
printf("\n\n"); continue;
}
get_sa(n,); get_he(n);
int l=,r=n,ans=;
while(l<=r)/*二分找答案*/
{
int mid=(l+r)/;
if(check(mid,tt,n))
{
ans=mid;
l=mid+;
}
else r=mid-;
}
if(!ans) {printf("?\n\n"); continue;}
putt(ans,tt,n); printf("\n");
/*ans是我们最后记录的成立的那个边界范围,就是左右的边界范围*/
}
return ;
}
Tristan Code 注释版
(非注释版,挺好理解的,思路清楚了就是细节的探索咯)
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
int sa[],Rank[],rsort[];
int cnt[],pos[],height[];
int a[],belong[],v[];
bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];}
char s[];
void get_sa(int n,int m)
{
int k=,p=,len;
for(int i=;i<=n;i++) Rank[i]=a[i];
memset(rsort,,sizeof(rsort));
for(int i=;i<=n;i++) rsort[Rank[i]]++;
for(int i=;i<=m;i++) rsort[i]+=rsort[i-];
for(int i=n;i>=;i--) sa[rsort[Rank[i]]--]=i;
for(int k=;k<n;k<<=)
{
len=;
for(int i=n-k+;i<=n;i++) pos[++len]=i;
for(int i=;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k;
for(int i=;i<=n;i++) cnt[i]=Rank[pos[i]];
memset(rsort,,sizeof(rsort));
for(int i=;i<=n;i++) rsort[cnt[i]]++;
for(int i=;i<=m;i++) rsort[i]+=rsort[i-];
for(int i=n;i>=;i--) sa[rsort[cnt[i]]--]=pos[i];
for(int i=;i<=n;i++) cnt[i]=Rank[i];
p=; Rank[sa[]]=;
for(int i=;i<=n;i++)
{
if(!cmp(sa[i],sa[i-],k)) p++;
Rank[sa[i]]=p;
}
if(p==n) break; m=p;
}
a[]=; sa[]=;
}
void get_he(int n)
{
int j,k=;
for(int i=;i<=n;i++)
{
if(Rank[i]==) continue;
j=sa[Rank[i]-];
if(k) k--;
while(a[j+k]==a[i+k]) k++;
height[Rank[i]]=k;
}
}
bool check(int mid,int k,int n)
{
int ans=;
memset(v,,sizeof(v)); v[]=;
if(!v[belong[sa[]]]) ans++;
v[belong[sa[]]]=;
for(int i=;i<=n;i++)
{
if(height[i]<mid)
{
memset(v,,sizeof(v)); v[]=;
ans=;
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;
}
else
{
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;
}
if(ans>=k)return true;
}
return false;
}
void putt(int x,int k,int n)
{
int ans=;
memset(v,,sizeof(v)); v[]=;
if(!v[belong[sa[]]]) ans++;
v[belong[sa[]]]=;
for(int i=;i<=n;i++)
{
if(height[i]<x)
{
if(ans>=k)
{
for(int j=sa[i-];j<=sa[i-]+x-;j++) printf("%c",a[j]+'a');
printf("\n");
}
memset(v,,sizeof(v)); v[]=;
ans=;
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;
}
else
{
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;
}
}
if(ans>=k)
{
for(int i=sa[n];i<=sa[n]+x-;i++) printf("%c",a[i]+'a');
printf("\n");
}
}
int main()
{
int t;
while(scanf("%d",&t)!=EOF && t)
{
int n=,tp=,tt=t/+;
for(int i=;i<=t;i++)
{
scanf("%s",s+);
for(int j=;j<=strlen(s+);j++)
{
a[++n]=s[j]-'a';
belong[n]=i;
}
a[++n]=tp; tp++;
}
if(t==)
{
for(int i=;i<n;i++) printf("%c",a[i]+'a');
printf("\n\n"); continue;
}
get_sa(n,); get_he(n);
int l=,r=n,ans=;
while(l<=r)
{
int mid=(l+r)/;
if(check(mid,tt,n))
{
ans=mid;
l=mid+;
}
else r=mid-;
}
if(!ans) {printf("?\n\n"); continue;}
putt(ans,tt,n); printf("\n");
}
return ;
}
Tristan Code 非注释版
/*二分搜索的时候直接记录答案*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
int sa[],Rank[],rsort[];
int cnt[],pos[],h[],ans[],anslen;
int a[],belong[],v[];
bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];}
char s[];
void get_sa(int n,int m)
{
int k=,p=,len;
for(int i=;i<=n;i++) Rank[i]=a[i];
memset(rsort,,sizeof(rsort));
for(int i=;i<=n;i++) rsort[Rank[i]]++;
for(int i=;i<=m;i++) rsort[i]+=rsort[i-];
for(int i=n;i>=;i--) sa[rsort[Rank[i]]--]=i;
for(int k=;k<n;k<<=)
{
len=;
for(int i=n-k+;i<=n;i++) pos[++len]=i;
for(int i=;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k;
for(int i=;i<=n;i++) cnt[i]=Rank[pos[i]];
memset(rsort,,sizeof(rsort));
for(int i=;i<=n;i++) rsort[cnt[i]]++;
for(int i=;i<=m;i++) rsort[i]+=rsort[i-];
for(int i=n;i>=;i--) sa[rsort[cnt[i]]--]=pos[i];
for(int i=;i<=n;i++) cnt[i]=Rank[i];
p=; Rank[sa[]]=;
for(int i=;i<=n;i++)
{
if(!cmp(sa[i],sa[i-],k)) p++;
Rank[sa[i]]=p;
}
if(p==n) break; m=p;
}
a[]=; sa[]=;
}
void get_he(int n)
{
int j,k=;
for(int i=;i<=n;i++)
{
if(Rank[i]==) continue;
j=sa[Rank[i]-];
if(k) k--;
while(a[j+k]==a[i+k]) k++;
h[Rank[i]]=k;
}
}
bool check(int mid,int k,int n)
{
int sum=; bool bk=;
memset(v,,sizeof(v)); v[]=;
if(!v[belong[sa[]]]) v[belong[sa[]]]=,sum++;
for(int i=;i<=n;i++)
{
if(h[i]<mid)
{
if(sum>=k)
{
if(!bk) anslen=;
ans[++anslen]=sa[i-];bk=;
}
memset(v,,sizeof(v)); v[]=; sum=;
if(!v[belong[sa[i]]]) v[belong[sa[i]]]=,sum++;
}
else if(!v[belong[sa[i]]]) v[belong[sa[i]]]=,sum++;
}
if(sum>=k)
{
if(!bk) anslen=;
ans[++anslen]=sa[n],bk=;
}
return bk;
}
void write(int x)
{
if(!x){printf("?\n\n"); return ;}
for(int i=;i<=anslen;i++)
{
for(int j=ans[i];j<=ans[i]+x-;j++) printf("%c",a[j]+'a');
printf("\n");
}
printf("\n");
}
int main()
{
int t;
while(scanf("%d",&t)!=EOF && t)
{
int n=,tp=,tt=t/+;
for(int i=;i<=t;i++)
{
scanf("%s",s+);
for(int j=;j<=strlen(s+);j++)
{
a[++n]=s[j]-'a';
belong[n]=i;
}
a[++n]=tp; tp++;
}
if(t==)
{
for(int i=;i<n;i++) printf("%c",a[i]+'a');
printf("\n\n"); continue;
}
get_sa(n,); get_he(n);
int l=,r=n,ans=;
while(l<=r)
{
int mid=(l+r)/;
if(check(mid,tt,n))
{
ans=mid;
l=mid+;
}
else r=mid-;
}
write(ans);
}
return ;
}
常数较小,较快 Code
后缀数组练习4:Life Forms的更多相关文章
- 后缀数组 UVA 11107 Life Forms
题目链接 题意:训练指南P223 分析:二分长度,把所有字符串连成一个字符串,中间用不同的字符分隔(这是为了保证匹配长度始终在一个字符串内).height数组分段,vis数组标记哪些字符串被访问了,如 ...
- UVA11107 Life Forms --- 后缀数组
UVA11107 Life Forms 题目描述: 求出出现在一半以上的字符串内的最长字符串. 数据范围: \(\sum len(string) <= 10^{5}\) 非常坑的题目. 思路非常 ...
- 后缀数组LCP + 二分 - UVa 11107 Life Forms
Life Forms Problem's Link Mean: 给你n个串,让你找出出现次数大于n/2的最长公共子串.如果有多个,按字典序排列输出. analyse: 经典题. 直接二分判断答案. 判 ...
- POJ3294 Life Forms —— 后缀数组 最长公共子串
题目链接:https://vjudge.net/problem/POJ-3294 Life Forms Time Limit: 5000MS Memory Limit: 65536K Total ...
- Poj 3294 Life Forms (后缀数组 + 二分 + Hash)
题目链接: Poj 3294 Life Forms 题目描述: 有n个文本串,问在一半以上的文本串出现过的最长连续子串? 解题思路: 可以把文本串用没有出现过的不同字符连起来,然后求新文本串的heig ...
- POJ3294 Life Forms(后缀数组)
引用罗穗骞论文中的话: 将n 个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求后缀数组.然后二分答案,用和例3 同样的方法将后缀分成若干组,判断每组的后缀是否出现在不小于k 个的原串中 ...
- POJ-3294-Life Forms(后缀数组-不小于 k 个字符串中的最长子串)
题意: 给定 n 个字符串,求出现在不小于 k 个字符串中的最长子串. 分析: 将 n 个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求后缀数组. 然后二分答案,将后缀分成若干组,判断 ...
- POJ 3294 Life Forms(后缀数组+二分答案)
[题目链接] http://poj.org/problem?id=3294 [题目大意] 求出在至少在一半字符串中出现的最长子串. 如果有多个符合的答案,请按照字典序输出. [题解] 将所有的字符串通 ...
- 2018.11.28 poj3294 Life Forms(后缀数组+双指针)
传送门 后缀数组经典题目. 我们先把所有的字符串都接在一起. 然后求出hththt数组和sasasa数组. 然后对于sasasa数组跑双指针统计答案. 如果双指针包括进去的属于不同字符串的数量达到了题 ...
随机推荐
- vueApp打包
本地打包测试 http-server是一个基于node.js的简单的,零配置的命令行http服务器.安装:npm install http-server -g使用:http-server [path] ...
- nodejs的npm命令无反应的解决方案
这二天用npm下载模块的时候输入npm命令完全无反应,不是加载的那种状态而是下标不停地在哪里闪...之后找解决方案,说要删除npmrc文件.强调:不是nodejs安装目录npm模块下的那个npmrc文 ...
- JavaWeb_(Hibernate框架)Hibernate中数据查询语句SQL基本用法
本文展示三种在Hibernate中使用SQL语句进行数据查询基本用法 1.基本查询 2.条件查询 3.分页查询 package com.Gary.dao; import java.util.List; ...
- android data binding jetpack VIIII 第一坑
<LinearLayout android:id="@+id/ll_item_home_page_pics" android:layout_width="wrap_ ...
- 上有传参下传json的接口调用
1.优化Myrequest import requests from conf.setting import log class MyRequest(): @staticmethod def post ...
- adb、pm命令操作apk包
1.adb shell pm list package 打印出来所有安装到手机上的APP包名 2.adb shell pm path com.xxx.xxx 找出安装后的包名应用的apk所在位置 3. ...
- centos7修改默认启动模式(图形/命令行)
centos7以后是这样的,7以前就是别的版本了 1.systemctl get-default命令获取当前模式 2.systemctl set-default graphical.target 修改 ...
- ubuntu19.04 安装workbench
1.首先下载安装这两个包: https://packages.ubuntu.com/cosmic/amd64/libssl1.0.0/download https://packages.ubuntu. ...
- VSCode查询快捷键对应功能技巧
- LC 351. Android Unlock Patterns
Given an Android 3x3 key lock screen and two integers m and n, where 1 ≤ m ≤ n ≤ 9, count the total ...