如果不存在模糊点,那么答案就是两个串的最长公共子串。

如果模糊点是某个串的开头或者结尾,那么可以暴力枚举另一个串中的某个前后缀更新答案。

否则,假设模糊点在第一个串里是$i$,在第二个串里是$j$,那么此时对答案的贡献为$lcp(i+1,j+1)+lcs(i-1,j-1)+1$。

将两个串用特殊字符拼接,求出正串的后缀数组以及反串的后缀数组,那么$lcp(i+1,j+1)+lcs(i-1,j-1)+1$等价于两个height数组的区间最小值的和。

考虑从大到小枚举第一个数,每次把所有大于等于它的部分全部合并,那么在另一个数组里显然只有相邻的后缀有用。

所以对于每个集合建立两棵平衡树,维护两个串在反串里的rank。

在启发式合并的时候,只需要在另一个串对应的平衡树里找到前驱后继然后更新答案即可。

时间复杂度$O(n\log^2n)$。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
using namespace std;
const int N=400010,M=200005;
char sa[N],sb[N],s[N];int na,nb,n,i,j,Log[N],ans;
struct DS{
char s[N];
int rk[N],sa[N],height[N],tmp[N],cnt[N],f[18][M];
void suffixarray(int n,int m){
int i,j,k;n++;
for(i=0;i<n*2+5;i++)rk[i]=sa[i]=height[i]=tmp[i]=0;
for(i=0;i<m;i++)cnt[i]=0;
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];
}
void build(){
int i,j;
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)swap(x,y);
return ask(x+1,y);
}
}A,B;
int q[M],f[M],ca[M],cb[M],size[M],g[M],v[M<<1],nxt[M<<1],ed,lcp;
set<int>TA[M],TB[M];
inline bool cmp(int x,int y){return A.height[x]>A.height[y];}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x,int y,int z){
f[x]=z;
if(A.sa[x]<na){
int j=A.sa[x]-2;
if(j>=0){
j=B.rk[n-1-j];
TA[z].insert(j);
set<int>::iterator k=TB[z].lower_bound(j);
if(k!=TB[z].end())ans=max(ans,lcp+B.lcp(j,*k));
if(k!=TB[z].begin())k--,ans=max(ans,lcp+B.lcp(j,*k));
}
}
if(A.sa[x]>na){
int j=A.sa[x]-2;
if(j>na){
j=B.rk[n-1-j];
TB[z].insert(j);
set<int>::iterator k=TA[z].lower_bound(j);
if(k!=TA[z].end())ans=max(ans,lcp+B.lcp(j,*k));
if(k!=TA[z].begin())k--,ans=max(ans,lcp+B.lcp(j,*k));
}
}
for(int i=g[x];i;i=nxt[i])if(v[i]!=y)dfs(v[i],x,z);
}
inline void merge(int x,int y){
x=f[x],y=f[y];
if(size[x]>size[y])swap(x,y);
if(ca[x]&&cb[y]||cb[x]&&ca[y])ans=max(ans,lcp-1);
ca[y]|=ca[x],cb[y]|=cb[x],size[y]+=size[x];
TA[x].clear(),TB[x].clear();
add(x,y),add(y,x);
dfs(x,y,y);
}
int main(){
for(i=2;i<N;i++)Log[i]=Log[i>>1]+1;
scanf("%s%s",sa,sb);
na=strlen(sa);
nb=strlen(sb);
if(na==1||nb==1)return puts("1"),0;
for(i=0;i<na;i++)s[n++]=sa[i];
s[n++]='#';
for(i=0;i<nb;i++)s[n++]=sb[i];
for(i=0;i<n;i++)A.s[i]=B.s[n-i-1]=s[i];
A.suffixarray(n,128);
B.suffixarray(n,128);
B.build();
for(i=1;i<=n;i++){
f[i]=i,size[i]=1;
if(A.sa[i]<na){
ca[i]=1;
j=A.sa[i]-2;
if(j>=0)TA[i].insert(B.rk[n-1-j]);
}
if(A.sa[i]>na){
cb[i]=1;
j=A.sa[i]-2;
if(j>na)TB[i].insert(B.rk[n-1-j]);
}
}
for(i=1;i<n;i++)q[i]=i+1;
sort(q+1,q+n,cmp);
for(i=1;i<n;i++)lcp=A.height[q[i]],merge(q[i]-1,q[i]);
j=A.rk[1];
for(lcp=N,i=j-1;i;i--){
lcp=min(lcp,A.height[i+1]);
if(A.sa[i]>na+1)ans=max(ans,lcp);
}
for(lcp=N,i=j+1;i<=n;i++){
lcp=min(lcp,A.height[i]);
if(A.sa[i]>na+1)ans=max(ans,lcp);
}
j=A.rk[na+2];
for(lcp=N,i=j-1;i;i--){
lcp=min(lcp,A.height[i+1]);
if(A.sa[i]&&A.sa[i]<na)ans=max(ans,lcp);
}
for(lcp=N,i=j+1;i<=n;i++){
lcp=min(lcp,A.height[i]);
if(A.sa[i]&&A.sa[i]<na)ans=max(ans,lcp);
}
j=B.rk[nb+2];
for(lcp=N,i=j-1;i;i--){
lcp=min(lcp,B.height[i+1]);
if(B.sa[i]&&B.sa[i]<nb)ans=max(ans,lcp);
}
for(lcp=N,i=j+1;i<=n;i++){
lcp=min(lcp,B.height[i]);
if(B.sa[i]&&B.sa[i]<nb)ans=max(ans,lcp);
}
j=B.rk[1];
for(lcp=N,i=j-1;i;i--){
lcp=min(lcp,B.height[i+1]);
if(B.sa[i]>nb+1)ans=max(ans,lcp);
}
for(lcp=N,i=j+1;i<=n;i++){
lcp=min(lcp,B.height[i]);
if(B.sa[i]>nb+1)ans=max(ans,lcp);
}
return printf("%d",ans+1),0;
}

  

BZOJ3145 : [Feyat cup 1.5]Str的更多相关文章

  1. BZOJ3145 [Feyat cup 1.5]Str 后缀树、启发式合并

    传送门--BZOJCH 考虑两种情况: 1.答案由一个最长公共子串+可能的一个模糊匹配位置组成.这个用SAM求一下最长公共子串,但是需要注意只出现在\(S\)的开头和\(T\)的结尾的子串是不能够通过 ...

  2. [BZOJ 3145][Feyat cup 1.5]Str 解题报告

    [Feyat cup 1.5]Str DescriptionArcueid,白姬,真祖的公主.在和推倒贵看电影时突然对一个问题产生了兴趣:我们都知道真祖和死徒是有类似的地方.那么从现代科学的角度如何解 ...

  3. Bzoj 3145 - [Feyat cup 1.5]Str

    bzoj 3145 - [Feyat cup 1.5]Str Description 给你两个长度\(10^5\)级别的串\(S, T\) 求\(S,T\)的最长模糊匹配公共子串 模糊匹配 : 至多一 ...

  4. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  5. Sam做题记录

    Sam做题记录 Hihocoder 后缀自动机二·重复旋律5 求一个串中本质不同的子串数 显然,答案是 \(\sum len[i]-len[fa[i]]\) Hihocoder 后缀自动机三·重复旋律 ...

  6. Python数据分析实例操作

    import pandas as pd #导入pandas import matplotlib.pyplot as plt #导入matplotlib from pylab import * mpl. ...

  7. eval(PHP 4, PHP 5)

    eval — 把字符串作为PHP代码执行 说明 mixed eval ( string $code_str ) 把字符串 code_str 作为PHP代码执行. 除了其他,该函数能够执行储存于数据库文 ...

  8. PHP 一句话木马

    eval 函数 eval() 函数把字符串按照 PHP 代码来计算 该字符串必须是合法的 PHP 代码,且必须以分号结尾 如果没有在代码字符串中调用 return 语句,则返回 NULL.如果代码中存 ...

  9. Codeforces Round #405 (rated, Div. 2, based on VK Cup 2017 Round 1) 菜鸡只会ABC!

    Codeforces Round #405 (rated, Div. 2, based on VK Cup 2017 Round 1) 全场题解 菜鸡只会A+B+C,呈上题解: A. Bear and ...

随机推荐

  1. spring 定时任务

    http://zywang.iteye.com/blog/949123 xml 需要根据情况酌情删减

  2. ShareSdk使用心得

    1. 微信和朋友圈:分享的时候设置了链接和图片,但就是不显示: 需要指明ShareType为WEB_PAGE 2. 需要完整添加 ShareSdk 的所需要的权限,不然分享闪退,并且不报异常:网络请求 ...

  3. sql server 的cpu使用率过高的分析

    有哪些SQL语句会导致CPU过高? 1.编译和重编译 编译是 Sql Server 为指令生成执行计划的过程.Sql Server 要分析指令要做的事情,分析它所要访问的表格结构,也就是生成执行计划的 ...

  4. Java 内存区域和GC机制

    目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...

  5. Dubbo超时和重连机制

    dubbo启动时默认有重试机制和超时机制.超时机制的规则是如果在一定的时间内,provider没有返回,则认为本次调用失败,重试机制在出现调用失败时,会再次调用.如果在配置的调用次数内都失败,则认为此 ...

  6. SQL链表查询 数据库为空

    查询出数据为空,解决方案:链表 对应字段长度不一致.

  7. C#在excel中添加超链接

    1.新建一个项目 2.给项目添加引用:Microsoft Excel 12.0 Object Library (2007版本) using Excel = Microsoft.Office.Inter ...

  8. 7-11使用UNION合并查询

    合并查询的语法: SELECT ...FROM  表名一 UNION SELECT ...FROM 表名二 合并查询的特点: 1: 合并表中的列的个数,数据类型数据类型相同或兼容. 2:UNION 默 ...

  9. WPF PRISM开发入门一( 初始化PRISM WPF程序)

    这篇博客将介绍在WPF项目中引入PRISM框架进行开发的一些基础知识.目前最新的PRISM的版本是Prism 6.1.0,可以在Github上获取PRISM的源码.这个系列的博客将选择PRISM 4. ...

  10. 静态内容生成器——Wyam

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:今天继续静态网站的话题,介绍我选用的一个使用.NET开发的静态内容生成器--Wyam. ...