写一种\(O(nm)\)的做法,也就是\(O(\sum 串长)\)的。

先通过差分转化,把每个数变成这个数与上一个数的差,第一个数去掉,答案就是最长公共子串+1

按照套路把所有串拼起来,中间加一个分隔符号,然后用DC3求出SA以及height

(DC3我也不会,蒯的TJJ的板子,因为一般用倍增SA足够了)

对每个后缀可以知道它在原来的第几个串,那么求出SA以后,答案就是一段区间\([l,r]\),即排名为\([l,r]\)的这些后缀,而且必须覆盖原来的所有串,长度就是这些后缀的LCP,就是一段height的最小值

枚举左端点,那么最小的右端点一定不小于上一个的最小右端点,所以直接维护右端点以及这一段区间包括原来每一个串的数量就好了

最小值可以直接单调队列维护

#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
typedef unsigned long long ulint;
namespace input
{
const int bufl = 1<<14;
char buf[bufl],*s=buf,*t=buf;
inline int fetch()
{
if(s==t){t=(s=buf)+fread(buf,1,bufl,stdin);if(s==t)return EOF;}
return *s++;
}
inline int gi()
{
register int a=0,b=1,c=fetch();
while(!isdigit(c))b^=c=='-',c=fetch();
while(isdigit(c))a=a*10+c-48,c=fetch();
return b?a:-a;
}
}
using input::gi;
const int N = 202007 , N3 = N*3 , maxalpha = 99999;
int wa[N3],wb[N3],wv[N3],buc[N3];
inline void bsort(int *s,int *a,int *b,int n,int m)
{
register int i;
for(i=0;i<n;i++)wv[i]=s[a[i]];
for(i=0;i<m;i++)buc[i]=0;
for(i=0;i<n;i++)buc[wv[i]]++;
for(i=1;i<m;i++)buc[i]+=buc[i-1];
for(i=n-1;i>=0;i--)b[--buc[wv[i]]]=a[i];
}
inline int c0(int *s,int a,int b)
{
return s[a]==s[b] && s[a+1]==s[b+1] && s[a+2]==s[b+2];
}
inline int c12(int k,int *s,int a,int b)
{
if(k==2)return s[a]<s[b] || (s[a]==s[b] && c12(k-1,s,a+1,b+1));
return s[a]<s[b] || (s[a]==s[b] && wv[a+1]<wv[b+1]);
}
#define f(x) ((x)/3+((x)%3==1?0:tb))
#define g(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
void dc3(int *s,int *sa,int n,int m)
{
int i,j,*sn=s+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
s[n]=s[n+1]=0;
for(i=0;i<n;i++)if(i%3!=0)wa[tbc++]=i;
bsort(s+2,wa,wb,tbc,m),bsort(s+1,wb,wa,tbc,m),bsort(s,wa,wb,tbc,m);
for(sn[f(wb[0])]=0,p=1,i=1;i<tbc;i++)sn[f(wb[i])]=c0(s,wb[i-1],wb[i])?p-1:p++;
if(p<tbc)dc3(sn,san,tbc,p);
else for(i=0;i<tbc;i++)san[sn[i]]=i;
for(i=0;i<tbc;i++)if(san[i]<tb)wb[ta++]=san[i]*3;
if(n%3==1)wb[ta++]=n-1;
bsort(s,wb,wa,ta,m);
for(i=0;i<tbc;i++)wv[wb[i]=g(san[i])]=i;
for(i=j=p=0;i<ta && j<tbc;p++)sa[p]=c12(wb[j]%3,s,wa[i],wb[j])?wa[i++]:wb[j++];
for(;i<ta;p++)sa[p]=wa[i++];
for(;j<tbc;p++)sa[p]=wb[j++];
}
#undef f
#undef g
void calheight(int *s,int *sa,int n,int *rk,int *ht)
{
register int i,j,k=0;
for(i=1;i<=n;++i)rk[sa[i]]=i;
for(i=0;i<n;ht[rk[i++]]=k)for(k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k] && k<n;++k);
return;
}
char s[N3];
int n,ss[N3],sa[N3],rk[N3],ht[N3];
int p[N3],S[N3];
int que[N3],hd,tl,cnt[N3];
int main()
{
int NN=gi(),n=0,sep=0;
if(NN==1)return printf("%d\n",gi()),0;
while(NN--){
int t=gi(),lst=gi();
while(--t){
int x=gi();
S[n++]=lst-x+10000;
lst=x;
}
S[n++]=++sep;
}
s[n]=0;
dc3(S,sa,n+1,20000);
calheight(S,sa,n,rk,ht);
int k=1;
for(int i=0;i<n;++i)if(S[i]>1000)p[i]=k;else ++k;//p[i]是后缀i属于原来的第几个串
int CNT=0,r=k,Mx=0;//cnt是原来每个串的出现次数,CNT是总的
hd=tl=0;
--k;
for(int i=k+1;i<=n;++i){
while(r<=n&&CNT<k){
if(cnt[p[sa[r]]]++==0)++CNT;
if(r){
while((hd^tl)&&ht[que[tl-1]]>ht[r])--tl;
que[tl++]=r;
}
++r;
}
if(CNT<k)break;
while((hd^tl)&&que[hd]<=i)++hd;
if(hd^tl)Mx=std::max(Mx,ht[que[hd]]);
if(--cnt[p[sa[i]]]==0)--CNT;
}
printf("%d\n",Mx+1);
return 0;
}

(大括号换行的是蒯的板子,不换行的也就是核心代码是我自己写的,DC3只是为了保证复杂度而已)

P2463 [SDOI2008]Sandy的卡片的更多相关文章

  1. 洛谷 P2463 [SDOI2008]Sandy的卡片 解题报告

    P2463 [SDOI2008]Sandy的卡片 题意 给\(n(\le 1000)\)串,定义两个串相等为"长度相同,且一个串每个数加某个数与另一个串完全相同",求所有串的最长公 ...

  2. bzoj4698 / P2463 [SDOI2008]Sandy的卡片

    P2463 [SDOI2008]Sandy的卡片 直接二分长度暴力匹配....... 跑的还挺快 (正解是后缀数组的样子) #include<iostream> #include<c ...

  3. P2463 [SDOI2008]Sandy的卡片[差分+串拼接后缀数组]

    P2463 [SDOI2008]Sandy的卡片 套路都差不多,都是差分后二分答案找lcp.只是这题要把多个串拼接起来成为一个大串,中间用某些值域中没有的数字相隔(最好间隔符都不一样想想为什么),排序 ...

  4. 洛谷P2463 [SDOI2008]Sandy的卡片(后缀数组SA + 差分 + 二分答案)

    题目链接:https://www.luogu.org/problem/P2463 [题意] 求出N个串中都出现的相同子串的最长长度,相同子串的定义如题:所有元素加上一个数变成另一个,则这两个串相同,可 ...

  5. Luogu P2463 [SDOI2008]Sandy的卡片

    题目链接 \(Click\) \(Here\) 真的好麻烦啊..事实证明,理解是理解,一定要认认真真把板子打牢,不然调锅的时候真的会很痛苦..(最好是八分钟能无脑把\(SA\)码对的程度\(QAQ\) ...

  6. 洛咕 P2463 [SDOI2008]Sandy的卡片

    哈希水过. 首先这是一段delta相同的序列,按照套路差分一下,b[i]=a[i]-a[i-1],然后就是这些序列的最长公共子段 由于数据范围很小,就可以二分,枚举第一个序列的子段然后每个子序列暴力c ...

  7. [洛谷P2463][SDOI2008]Sandy的卡片

    题目大意:有$n$个字符串,求这$n$个字符串中最长的相似公共字串,相似的定义是加上一个数后相同 题解:差分,建广义后缀自动机,然后求出每个点在多少个字符串中出现过,若在$n$个中都出现,就更新答案 ...

  8. BZOJ 4698: Sdoi2008 Sandy的卡片

    4698: Sdoi2008 Sandy的卡片 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 106  Solved: 40[Submit][Stat ...

  9. BZOJ 4698: Sdoi2008 Sandy的卡片 [后缀自动机]

    4698: Sdoi2008 Sandy的卡片 题意:差分后就是多个串LCS SAM+map大法好 模板打错 智力-2 #include <iostream> #include <c ...

随机推荐

  1. 重学C语言---03数据和C

    1.数据的必要性.数据使我们生活中不可缺少的东西,程序也是如次,离不开数据.将文字.图片和单词等输入到算计,将其展现出来或者做一系列操作等. 2.实例程序. /*rhodium.c--用金属铑衡量体重 ...

  2. 【SPL标准库专题(10)】SPL Exceptions

    嵌套异常 了解SPL异常之前,我们先了解一下嵌套异常.嵌套异常顾名思义就是异常里面再嵌套异常,一个异常抛出,在catch到以后再抛出异常,这时可以通过Exception基类的getPrevious方法 ...

  3. 互联网,IT,大数据,机器学习,AI知识tag云

    互联网基础: tcp/ip网络,linux运维,DNS,ipv6 web前端: javascript, es6, 组件化开发, vuejs, angularjs, react html5, css3, ...

  4. angular、jquery、vue 的区别与联系

    angular和jquery的区别 angular中是尽量避免操作DOM, angular是基于数据驱动, 适合做数据操作比较繁琐的项目,angular适用于单页面开发,是一个比较完善的mvvm框架, ...

  5. "==" equals和hashCode的联系和区别

    写这篇文章的目的是自己彻底把三者搞清楚,也希望对你有所帮助. 1."=="运算符对与基本类型(int long float double boolean byte char sho ...

  6. python第四十四课——继承性之单继承

    2.继承性 继承: 使用场景: 1).生活层面:... 2).计算机层面: 两部分组成,一部分我们称为父类(基类.超类.superclass),另一部分我们称为子类(派生类.subclass), 子类 ...

  7. 协程运行原理猜测: async/await

    1.根据await调用链寻找最终的生产者或服务提供者: 2.请求服务: 3.进行执行环境切换,跳出顶层函数(第一个无await修饰的函数),执行后面的语句: 4.服务完成,将服务数据复制给最底层的aw ...

  8. $Gauss$消元

    $Gauss$消元 今天金牌爷来问我一个高消的题目,我才想起来忘了学高消... 高斯消元用于解线性方程组,也就是形如: $\left\{\begin{matrix}a_{11}x_1+a_{12}x_ ...

  9. Maven单独构建多模块项目中的单个模块

    Maven单独构建多模块项目中的单个模块   说明: 1.可能存在的场景,多模块项目没有互相引用,那么此时可以单独构建单个项目,指定到子模块的pom.xml文件即可完成编译. 2.如果多模块项目各自都 ...

  10. 5、JUC--实现 Callable 接口

    Callable接口  Java 5.0 在 java.util.concurrent 提供了一个新的创建执行 线程的方式:Callable 接口  Callable 接口类似于 Runnable ...