You are the King of Byteland. Your agents have just intercepted a batch of encrypted enemy messages concerning the date of the planned attack on your island. You immedietaly send for the Bytelandian Cryptographer, but he is currently busy eating popcorn and claims that he may only decrypt the most important part of the text (since the rest would be a waste of his time). You decide to select the fragment of the text which the enemy has strongly emphasised, evidently regarding it as the most important. So, you are looking for a fragment of text which appears in all the messages disjointly at least twice. Since you are not overfond of the cryptographer, try to make this fragment as long as possible.

Input

The first line of input contains a single positive integer t<=10, the number of test cases. t test cases follow. Each test case begins with integer n (n<=10), the number of messages. The next n lines contain the messages, consisting only of between 2 and 10000 characters 'a'-'z', possibly with some additional trailing white space which should be ignored.

Output

For each test case output the length of longest string which appears disjointly at least twice in all of the messages.

Example

Input:
1
4
abbabba
dabddkababa
bacaba
baba Output:
2

(in the example above, the longest substring which fulfills the requirements is 'ba')

题意:

每个字符串至少出现两次且不重叠的最长子串的长度

思路:

二分答案,按height分组,记录同一组内同一字符串的起点的最大值和最小值,在判断最大值减最小值是否大于mid即可。

#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime> #define fuck(x) cerr<<#x<<" = "<<x<<endl;
#define debug(a, x) cerr<<#a<<"["<<x<<"] = "<<a[x]<<endl;
#define ls (t<<1)
#define rs ((t<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = ;
const int maxm = ;
const int inf = 0x3f3f3f3f;
const ll Inf = ;
const int mod = ;
const double eps = 1e-;
const double pi = acos(-); int s[maxn];
int len, Rank[maxn], sa[maxn], tlen, tmp[maxn]; bool compare_sa(int i, int j) {
if (Rank[i] != Rank[j]) { return Rank[i] < Rank[j]; }
//如果以i开始,长度为k的字符串的长度,已经超出了字符串尾,那么就赋值为-1
//这是因为,在前面所有数据相同的情况下,字符串短的字典序小.
int ri = i + tlen <= len ? Rank[i + tlen] : -inf;
int rj = j + tlen <= len ? Rank[j + tlen] : -inf;
return ri < rj;
} void construct_sa() {
//初始的RANK为字符的ASCII码
for (int i = ; i <= len; i++) {
sa[i] = i;
Rank[i] = i < len ? s[i] : -inf;
}
for (tlen = ; tlen <= len; tlen *= ) {
sort(sa, sa + len + , compare_sa);
tmp[sa[]] = ;
//全新版本的RANK,tmp用来计算新的rank
//将字典序最小的后缀rank计为0
//sa之中表示的后缀都是有序的,所以将下一个后缀与前一个后缀比较,如果大于前一个后缀,rank就比前一个加一.
//否则就和前一个相等.
for (int i = ; i <= len; i++) {
tmp[sa[i]] = tmp[sa[i - ]] + (compare_sa(sa[i - ], sa[i]) ? : );
}
for (int i = ; i <= len; i++) {
Rank[i] = tmp[i]; }
}
} int height[maxn]; void construct_lcp() {
// for(int i=0;i<=n;i++){Rank[sa[i]]=i;}
int h = ;
height[] = ;
for (int i = ; i < len; i++) {//i为后缀数组起始位置
int j = sa[Rank[i] - ];//获取当前后缀的前一个后缀(排序后)
if (h > )h--;
for (; j + h < len && i + h < len; h++) {
if (s[j + h] != s[i + h])break;
}
height[Rank[i]] = h;
}
} int st[maxn][]; void rmq_init() {
for (int i = ; i <= len; i++) {
st[i][] = height[i];
}
int l = ;
for (int i = ; l <= len; i++) {
for (int j = ; j + l / <= len; j++) {
st[j][i] = min(st[j][i - ], st[j + l / ][i - ]);
}
l <<= ;
}
} int ask_min(int i, int j) {
int k = int(log(j - i + 1.0) / log(2.0));
return min(st[i][k], st[j - ( << k) + ][k]);
} int lcp(int a, int b)//此处参数是,原字符串下标
{
a = Rank[a], b = Rank[b];
if (a > b)
swap(a, b);
return ask_min(a + , b);
} char str[maxn];
int intr[maxn];
int mx[];
int mn[];
int n;
bool check(int mid){
if(mid==){ return true;}
memset(mx,,sizeof(mn));
memset(mn,0x3f,sizeof(mn));
for(int i=;i<=len;i++){
int prestart = lower_bound(intr+,intr++n,sa[i-])-intr;
int start = lower_bound(intr+,intr++n,sa[i])-intr;
// cout<<height[i]<<endl;
if(height[i]>=mid){
mn[start]=min(mn[start],sa[i]);
mx[start]=max(mx[start],sa[i]);
mn[prestart]=min(mn[prestart],sa[i-]);
mx[prestart]=max(mx[prestart],sa[i-]);
}else{
bool flag=true;
// fuck(n)
for(int j=;j<=n;j++){
// cerr<<mx[j]<<" "<<mn[j]<<endl;
if(mx[j]-mn[j]<mid){flag=false;}
}
if(flag){return true;}
memset(mx,,sizeof(mn));
memset(mn,0x3f,sizeof(mn));
}
}
return false;
} int main() {
// ios::sync_with_stdio(false);
// freopen("in.txt", "r", stdin); int cases=; int T;
scanf("%d",&T);
while (T--){
scanf("%d",&n);
cases++;
len=;
int lenx = ;
for(int i=;i<=n;i++){
scanf("%s",str);
int l=strlen(str);
lenx = max(lenx,l);
for(int j=;j<l;j++){
s[len++]=(int)str[j]-'a'+;
}
s[len++]=+i;
intr[i]=len-;
} construct_sa();
construct_lcp(); // fuck(check(4));
int l=,r=lenx;
int ans=;
while (r>=l){
int mid=(l+r)/;
// cout<<mid<<endl;
// cout<<l<<" "<<r<<endl;
if(check(mid)){
ans=mid;
l=mid+;
}else{
r=mid-;
}
}
printf("%d\n",ans);
} return ;
}

SPOJ - PHRASES Relevant Phrases of Annihilation (后缀数组)的更多相关文章

  1. SPOJ - PHRASES Relevant Phrases of Annihilation —— 后缀数组 出现于所有字符串中两次且不重叠的最长公共子串

    题目链接:https://vjudge.net/problem/SPOJ-PHRASES PHRASES - Relevant Phrases of Annihilation no tags  You ...

  2. SPOJ 220 Relevant Phrases of Annihilation(后缀数组+二分答案)

    [题目链接] http://www.spoj.pl/problems/PHRASES/ [题目大意] 求在每个字符串中出现至少两次的最长的子串 [题解] 注意到这么几个关键点:最长,至少两次,每个字符 ...

  3. SPOJ 220 Relevant Phrases of Annihilation(后缀数组)

    You are the King of Byteland. Your agents have just intercepted a batch of encrypted enemy messages ...

  4. SPOJ PHRASES Relevant Phrases of Annihilation(后缀数组 + 二分)题解

    题意: 给\(n\)个串,要你求出一个最长子串\(A\),\(A\)在每个字串至少都出现\(2\)次且不覆盖,问\(A\)最长长度是多少 思路: 后缀数组处理完之后,二分这个长度,可以\(O(n)\) ...

  5. SPOJ220 Relevant Phrases of Annihilation(后缀数组)

    引用罗穗骞论文中的话: 先将n 个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求后缀数组.然后二分答案,再将后缀分组.判断的时候,要看是否有一组后缀在每个原来的字符串中至少出现两次,并 ...

  6. SPOJ - PHRASES Relevant Phrases of Annihilation

    传送门:SPOJ - PHRASES(后缀数组+二分) 题意:给你n个字符串,找出一个最长的子串,他必须在每次字符串中都出现至少两次. 题解:被自己蠢哭...记录一下自己憨憨的操作,还一度质疑评测鸡( ...

  7. 【SPOJ 220】 PHRASES - Relevant Phrases of Annihilation

    [链接]h在这里写链接 [题意]     给你n(n<=10)个字符串.     每个字符串长度最大为1e4;     问你能不能找到一个子串.     使得这个子串,在每个字符串里面都不想交出 ...

  8. SPOJ 694 Distinct Substrings/SPOJ 705 New Distinct Substrings(后缀数组)

    Given a string, we need to find the total number of its distinct substrings. Input T- number of test ...

  9. SPOJ SUBST1 New Distinct Substrings(后缀数组 本质不同子串个数)题解

    题意: 问给定串有多少本质不同的子串? 思路: 子串必是某一后缀的前缀,假如是某一后缀\(sa[k]\),那么会有\(n - sa[k] + 1\)个前缀,但是其中有\(height[k]\)个和上一 ...

随机推荐

  1. Python中的动态继承

    所谓动态继承,是指代码运行时再决定某个类的父类.某些场景下会用到,比如threading.Thread和multiprocessing.Process这两个类有很多同名的接口,可以实现某个子类动态继承 ...

  2. 微信小程序错误——mpvue小程序:未找到 app.json 中的定义的 pages "pages/XXX/XXX" 对应的 WXML 文件

    背景 在刚开始学习开发小程序时,使用微信开发工具在app.json建立页面,写好配置文件名称后,应该会自动生成页面的4个文件,结果没有生成文件,反而报错:mpvue小程序:未找到 app.json 中 ...

  3. springboot项目启动,但是访问报404错误

    启动类Application上加了@ComponentScan(basePackages = {})这个注解导致controller扫描不到导致的,如果加了这个注解,springboot就不会扫描Ap ...

  4. 对The Curse of Dimensionality(维度灾难)的理解

    一个特性:低维(特征少)转向高维的过程中,样本会变的稀疏(可以有两种理解方式:1.样本数目不变,样本彼此之间距离增大.2.样本密度不变,所需的样本数目指数倍增长). 高维度带来的影响: 1.变得可分. ...

  5. AT3728 Squirrel Migration

    AT3728 Squirrel Migration 就是给每个点分配两个匹配点(自环除外) 考虑最大值 考虑极限情况:每个边的贡献是min(sz[u],sz[v])*2 证明存在方案: 发现,如果哪边 ...

  6. Java练习 SDUT-1253_进制转换

    进制转换 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 输入一个十进制数N,将它转换成R进制数输出. Input 输入 ...

  7. oracle函数 COALESCE(c1, c2, ...,cn)

    [功能]返回列表中第一个非空的表达式,如果所有表达式都为空值则返回1个空值 [参数]c1, c2, ...,cn,字符型/数值型/日期型,必须类型相同或null [返回]同参数类型 [说明]从Orac ...

  8. 现代IM系统中的消息系统架构 - 模型篇

    前言 在架构篇中我们介绍了现代IM消息系统的架构,介绍了Timeline的抽象模型以及基于Timeline模型构建的一个支持『消息漫游』.『多端同步』和『消息检索』多种高级功能的消息系统的典型架构.架 ...

  9. JS判断在哪一端浏览器打开

    <script src="js/jquery-2.2.3.min.js"></script> <script> var browser = { ...

  10. python命令之m参数 局域网传输

    在命令行中使用python时,python支持在其后面添加可选参数. python命令的可选参数有很多,例如:使用可选参数h可以查询python的帮助信息: 可选参数m 下面我们来说说python命令 ...