BZOJ3654 : 图样图森破

考虑枚举回文中心,然后向两边扩展,当匹配到当前串的边界的时候,枚举下一个串接上。

这个过程可以通过记忆化搜索来完成,设:

$f[i][0]$表示对于$i$这个位置,$[i,串结尾]$等待匹配的最长回文子串。

$f[i][1]$表示对于$i$这个位置,$[串开头,i]$等待匹配的最长回文子串。

如果在转移的过程中发现两个串都已经匹配到了边界,或者转移有环,那么说明答案无限。

用后缀数组支持lcp的询问,时间复杂度$O(nL+L\log L)$。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=400010,M=105;
int n,m,i,j,k,x,y,st[M],en[M],from[N],f[N][2],v[N][2],vis[N][2],ans;char a[N],s[N];
namespace SA{
int n,rk[N],sa[N],height[N],tmp[N],cnt[N],Log[N],f[18][N];char s[N];
void build(int n,int m){
  int i,j,k;n++;
  for(i=0;i<n;i++)cnt[rk[i]=s[i]]++;
  for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
  for(i=0;i<n;i++)sa[--cnt[rk[i]]]=i;
  for(k=1;k<=n;k<<=1){
    for(i=0;i<n;i++){
      j=sa[i]-k;
      if(j<0)j+=n;
      tmp[cnt[rk[j]]++]=j;
    }
    sa[tmp[cnt[0]=0]]=j=0;
    for(i=1;i<n;i++){
      if(rk[tmp[i]]!=rk[tmp[i-1]]||rk[tmp[i]+k]!=rk[tmp[i-1]+k])cnt[++j]=i;
      sa[tmp[i]]=j;
    }
    memcpy(rk,sa,n*sizeof(int));
    memcpy(sa,tmp,n*sizeof(int));
    if(j>=n-1)break;
  }
  for(j=rk[height[i=k=0]=0];i<n-1;i++,k++)
    while(~k&&s[i]!=s[sa[j-1]+k])height[j]=k--,j=rk[sa[j]+1];
  for(i=2;i<n;i++)Log[i]=Log[i>>1]+1;
  for(i=1;i<n;i++)f[0][i]=height[i];
  for(j=1;j<18;j++)for(i=1;i+(1<<j)-1<n;i++)f[j][i]=min(f[j-1][i],f[j-1][i+(1<<(j-1))]);
}
inline int ask(int x,int y){
  int k=Log[y-x+1];
  return min(f[k][x],f[k][y-(1<<k)+1]);
}
inline int lcp(int x,int y){
  if(x==y)return N;
  x=rk[x],y=rk[y];
  if(x>y)swap(x,y);
  return ask(x+1,y);
}
}
inline int query(int x,int y){
  return min(SA::lcp(x,m-1-y),min(en[from[x]]-x,y-st[from[y]])+1);
}
void getinf(){
  puts("Infinity");
  exit(0);
}
int dfs(int u,int p){
  if(vis[u][p])getinf();
  if(v[u][p])return f[u][p];
  v[u][p]=vis[u][p]=1;
  int&ret=f[u][p];
  if(!p){
    for(int i=1;i<=n;i++){
      int k=query(u,en[i]);
      int x=u+k-1,y=en[i]-k+1;
      if(x<en[from[u]]&&y>st[i])ret=max(ret,k*2);
      else if(x==en[from[u]]&&y==st[i])getinf();
      else if(x==en[from[u]])ret=max(ret,k*2+dfs(y-1,1));
      else ret=max(ret,k*2+dfs(x+1,0));
    }
  }else{
    for(int i=1;i<=n;i++){
      int k=query(st[i],u);
      int x=u-k+1,y=st[i]+k-1;
      if(x>st[from[u]]&&y<en[i])ret=max(ret,k*2);
      else if(x==st[from[u]]&&y==en[i])getinf();
      else if(x==st[from[u]])ret=max(ret,k*2+dfs(y+1,0));
      else ret=max(ret,k*2+dfs(x-1,1));
    }
  }
  vis[u][p]=0;
  return ret;
}
int main(){
  scanf("%d",&n);
  for(i=1;i<=n;i++){
    scanf("%s",a);
    st[i]=m;
    for(j=0;a[j];j++)from[m]=i,s[m++]=a[j];
    en[i]=m-1;
  }
  m<<=1;
  for(i=0,j=m-1;i<j;i++,j--)s[j]=s[i],from[j]=from[i];
  for(SA::n=m,i=0;i<m;i++)SA::s[i]=s[i];
  SA::build(m,128);
  for(i=1;i<=n;i++)ans=max(ans,max(dfs(st[i],0),dfs(en[i],1)));
  for(i=1;i<=n;i++){
    for(j=st[i];j<=en[i];j++){
      k=query(j,j);
      x=j-k+1,y=j+k-1;
      if(x>st[i]&&y<en[i])ans=max(ans,k*2-1);
      else if(x==st[i]&&y==en[i])getinf();
      else if(x==st[i])ans=max(ans,k*2-1+dfs(y+1,0));
      else ans=max(ans,k*2-1+dfs(x-1,1));
    }
    for(j=st[i];j<en[i];j++){
      k=query(j+1,j);
      x=j-k+1,y=j+k;
      if(x>st[i]&&y<en[i])ans=max(ans,k*2);
      else if(x==st[i]&&y==en[i])getinf();
      else if(x==st[i])ans=max(ans,k*2+dfs(y+1,0));
      else ans=max(ans,k*2+dfs(x-1,1));
    }
  }
  return printf("%d",ans),0;
}

  

时间: 2024-08-09 11:57:41

BZOJ3654 : 图样图森破的相关文章

程序员的高薪是编程语言决定的么?图样图森破啊。

写完发现有点像成功学,但是是实话实说 为什么会有黑.社会,本质上还是不抱团就不能活,为什么要打打杀杀,因为生活资源窘迫,不拼争就没有生存的价值,java和c#语言之争就像两个社团争斗,之前是vb和delphi,java和c++,但是不争斗会灭亡么,显然这是个伪命题,其实搞python,ruby,php或者perl的开发者都活着好好的,比大多数java开发者活的要轻松,那为什么社团之间的争斗在各类流行语言之间比较常见呢,这是一个容易引起误会的答案,我只能说呵呵了,也可能因为太流行了吧. 某类程序员

告诉我图样图森破的两道简单C++笔试题

今晚刷了一大堆的笔试题,中规中矩,但是有两道做得很快但是都错了的题目,印象深刻. (要找工作的大四渣有没有共鸣,在学校明明很努力,但是总是跟不上时代,没有厉害的项目,也没有过人的竞赛成绩,内推屡屡失败,前天阿里巴巴在线笔试也被虐死,真心迷惘,唯独刷题搞笔试了.) 第一道题是关于宏定义的. #include<iostream> using namespace std; #define fun(n) (n-1)*n int main() { int x=3; cout<<fun(x+3

大公司里开发部署前端代码

作者:张云龙链接:https://www.zhihu.com/question/20790576/answer/32602154来源:知乎著作权归作者所有,转载请联系作者获得授权. 前百度工程师,曾负责百度 前端集成解决方案 的核心设计与开发工作.我现在称这个领域为[前端工程].没错,这是我最爱唠叨的问题域. 这是一个非常有趣的 非主流前端领域,这个领域要探索的是如何用工程手段解决前端开发和部署优化的综合问题,入行到现在一直在学习和实践中. 在我的印象中,facebook是这个领域的鼻祖,有兴趣

宏------进阶

宏定义的黑魔法 - 宏菜鸟起飞手册 宏定义在C系开发中可以说占有举足轻重的作用.底层框架自不必说,为了编译优化和方便,以及跨平台能力,宏被大量使用,可以说底层开发离开define将寸步难行.而在更高层级进行开发时,我们会将更多的重心放在业务逻辑上,似乎对宏的使用和依赖并不多.但是使用宏定义的好处是不言自明的,在节省工作量的同时,代码可读性大大增加.如果想成为一个能写出漂亮优雅代码的开发者,宏定义绝对是必不可少的技能(虽然宏本身可能并不漂亮优雅XD).但是因为宏定义对于很多人来说,并不像业务逻辑那

如何提高程序员的逼格

还在整天为怎样才能更像一个IT精英而争吵的吐沫横飞的人们,我只能说你们图样图森破啊,看了下面的话,你应该自惭形秽,其实就是你们这些愚蠢的人类拉低了程序员的整体逼格,呵呵. 1.着装一个牛逼的程序员是根本没有时间打理自己外貌的.发型就要像爱因斯坦一样,顶着一脑袋鸡窝,凌乱蓬松美,给人随时能从头发里掏出一个鸡蛋的感觉.胡子一大把,彰显自信又从容,不近视则以,近视就要戴酒瓶底子那么厚的大眼镜,一种科研工作者的风格.逼程序员对自己着装是有高要求的.无论是春夏秋冬,白天晚上,刮风下雨,一个牛逼的程序员都要

关于数据库表字段冗余

今天因为数据库表设计的问题,被@红薯 说了一通.暴露了自己设计的几个问题,想通之后,发现果然自己还是图样图森破啊!这里挑一个很有想法的问题出来说. 假设有个场景.有这么几个表,我是这么设计的. 用户表[user]:id,userName 项目表[project]:id,projectName, user_id 版本表[version]:id,versionName,project_id 分类表[category]:id,categoryName,version_id 内容表[content]:i

备战软考(4) 软考下午题攻略

软考的全称是全国计算机技术与软件专业技术资格(水平)考试,而我们今天讨论的是其中的中级职称的一个科目----软件设计师.这个级别的考试主要分为两大块基础知识和应用技术,分别在考试当天的上午和下午进行测试. 对于基础知识这块,因为考查的知识面很广,也很细,个人而言无法找到一个行之有效的办法能让你迅速的提高上午题的成绩,因此就不在这里总结了,我们要做的就是看书,做题,再看书,再做题,然后接着看书,在看书与做题的反复中,一个一个的消灭自己的知识盲点和填补知识漏洞,这样慢慢的也许会有提升,但不要企图短时

转的es6 =&gt;函数

原文地址 箭头函数=>无疑是ES6中最受关注的一个新特性了,通过它可以简写 function 函数表达式,你也可以在各种提及箭头函数的地方看到这样的观点--"=> 就是一个新的 function". 箭头函数的句法规则甚至早已延伸到各项标准和技术文档中去了,虽然它早已不稀奇,却给我们一种刚刚发现的新鲜感. 粉我的人都知道俺因为某些原因不怎么喜欢 => 的语法,不过别担心,本文并非讲述我为何不喜欢它,如果你对这个观点感兴趣,可以查看我<YDKJS:ES6 &

网络云盘的存储机制

之前对于网络云盘的存储机制有过猜测,今天看了网上的博文分析(下面贴有原文),大部分证实我的猜想.总结来说,目前国内的网络云盘的存储机制主要从以下三点着手: 对于用户使用习惯和行为模式进行调研和分析,可以大致分析正常用户一天/一月/一年的平均存储容量大小需求: 采用存储集群(云计算技术解决)和分布式存储等技术来实现用户文件的存储,用以解决用户突发性存储需求和需求缓慢增长的问题: 针对用户重复性的存储的问题,采用计算存储文件MD5值并只保存唯一文件的方式解决,通过诱导用户使用客户端上传来分担计算MD