传送门

后缀数组经典题目。

我们先把所有的字符串都接在一起。

然后求出hththt数组和sasasa数组。

然后对于sasasa数组跑双指针统计答案。

如果双指针包括进去的属于不同字符串的数量达到了题目给出的限制我们就更新答案并不断右移左指针。

如果没有达到限制就一直右移右指针。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<algorithm>
#define ri register int
using namespace std;
const int N=2e5+5;
int rk[N],sa[N],sa2[N],ht[N],n,m,len[N],tt,tot[N],st[N][18],Log[N],idx[N];
char s[N],t[N];
vector<int>ans;
inline void Sort(){
	static int cnt[N];
	for(ri i=1;i<=m;++i)cnt[i]=0;
	for(ri i=1;i<=n;++i)++cnt[rk[i]];
	for(ri i=2;i<=m;++i)cnt[i]+=cnt[i-1];
	for(ri i=n;i;--i)sa[cnt[rk[sa2[i]]]--]=sa2[i];
}
inline void getsa(){
	for(ri i=1;i<=n;++i)rk[i]=(int)s[i],sa2[i]=i;
	m=122,Sort();
	for(ri w=1,p=0;m^n;w<<=1,p=0){
		for(ri i=n-w+1;i<=n;++i)sa2[++p]=i;
		for(ri i=1;i<=n;++i)if(sa[i]>w)sa2[++p]=sa[i]-w;
		Sort(),swap(sa2,rk),rk[sa[1]]=p=1;
		for(ri i=2;i<=n;++i)rk[sa[i]]=(sa2[sa[i]]==sa2[sa[i-1]]&&sa2[sa[i]+w]==sa2[sa[i-1]+w])?p:++p;
		m=p;
	}
	for(ri i=1,j,k=0;i<=n;ht[rk[i++]]=k)for(k?--k:k,j=sa[rk[i]-1];s[i+k]==s[j+k];++k);
	for(ri i=1;i<=n;++i)st[i][0]=ht[i];
	for(ri j=1;j<=17;++j)for(ri i=1;i+(1<<j)<=n;++i)st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
inline int rmq(int l,int r){return min(st[l][Log[r-l+1]],st[r-(1<<Log[r-l+1])+1][Log[r-l+1]]);}
inline bool check(int a,int b,int len){
	a=rk[a],b=rk[b];
	if(a>b)a^=b,b^=a,a^=b;
	return rmq(a+1,b)<len;
}
inline void solve(){
	ri sig=0,mxlen=0,lim=(tt>>1)+1,l=tt,r=tt-1;
	ans.clear();
	for(int i=1;i<=tt;++i)tot[i]=0;
	for(;;++l){
		while(sig<lim&&r<len[tt]){
			++r,++tot[idx[sa[r]]];
			if(tot[idx[sa[r]]]==1)++sig;
		}
		if(sig^lim)break;
		while(sig==lim){
			--tot[idx[sa[l]]];
			if(!tot[idx[sa[l]]]){
				ri tmp=rmq(l+1,r);
				if(tmp){
					if(tmp>mxlen)ans.clear(),ans.push_back(sa[l]),mxlen=tmp;
					else if(tmp==mxlen)ans.push_back(sa[l]);
				}
				--sig;
				break;
			}
			++l;
		}
	}
	if(!mxlen)puts("?");
	else for(ri i=0;i<ans.size();++i)if(!i||check(ans[i],ans[i-1],mxlen)){for(ri j=ans[i],k=1;k<=mxlen;++k,++j)cout<<s[j];puts("");}
}
int main(){
	freopen("lx.in","r",stdin);
	freopen("owon.out","w",stdout);
	Log[0]=-1;
	for(ri i=1;i<=200000;++i)Log[i]=Log[i>>1]+1;
	while(scanf("%d",&tt),n=0,tt){
		for(ri i=1;i<=tt;++i){
			scanf("%s",t),len[i]=strlen(t),s[++n]=(char)i;
			for(ri j=0;j<len[i];++j)s[++n]=t[j];
			++len[i],len[i]+=len[i-1];
		}
		for(ri i=1;i<=tt;++i)for(ri j=len[i-1]+1;j<=len[i];++j)idx[j]=i;
		getsa();
		solve(),puts("");
	}
	return 0;
}

最新文章

  1. 【emWin】例程四:显示文本
  2. 关于WPF程序启动性能
  3. 路径 dirname(__FILE__)
  4. angularjs ng-select ng-options 默认选中项.
  5. Spring通过工厂创建实例的注意事项
  6. BroadcastReceiver总结
  7. python实现 双向循环链表
  8. Android版数据结构与算法(七):赫夫曼树
  9. QComboBox使用方法,QComboBox详解
  10. PE知识复习之PE的导出表
  11. springBoot总结
  12. Python学习笔记【第十一篇】:Python面向对象高级
  13. POJ 2942Knights of the Round Table(tarjan求点双+二分图染色)
  14. pytorch学习-WHAT IS PYTORCH
  15. RBAC模型
  16. 怎么让 Lua 5.3.4 支持中文变量名和中文函数名
  17. 数据格式XML、JSON详解
  18. C++中构造函数和析构函数的调用顺序
  19. PAT-GPLT训练集 L1-043 阅览室
  20. cat命令合并多个txt文件

热门文章

  1. 对于装office 365时,visio不兼容的解决
  2. lombok ------让代码更简洁方便
  3. gearman的持久化,以mysql的方式
  4. js 横屏 竖屏 相关代码 与知识点
  5. linux下的压缩命令
  6. SharpSvn 调用在运行时提示加载程序集出错,或有依赖项
  7. jQuery Dom对象操作 增、删、改、复制、包裹
  8. LOADRUNNER重装经验
  9. RabbitMQ 的基本介绍
  10. if __name__ == &#39;__main__的理解