洛谷题目页面传送门 & CodeForces题目页面传送门

给定\(2\)个字符串\(a,b,|a|=n,|b|=m\),求最长的既在\(a\)中出现恰好\(1\)次又在\(b\)中出现恰好\(1\)次的非空字符串的长度,如果不存在输出\(-1\)。

\(n,m\in[1,5000]\)。

emmm,数据范围很不友好,\(\mathrm O(nm)\)带\(\log\)都不行。。。

考虑枚举\(a\)的子串。枚举子串可以转化为枚举所有后缀的所有前缀,这样一来就有了“前缀”这个东西可以利用。

我们在枚举\(a\)的后缀\(a_{i\sim n}\)的时候,令\(c=a_{i\sim n}+\texttt{!}+a+\texttt{@}+b,s=|c|\)。对\(c\)跑一遍Z算法(如果聪明的读者还不知道Z算法是什么,please点击这个),就可以知道后缀\(a_{i\sim n}\)在\(a,b\)中的出现情况了。

我们先把\(z_{c,n-i+3\sim 2n-i+2},z_{c,2n-i+4\sim s}\)分别装在\(2\)个桶\(buc1,buc2\)里,即\(buc1_j\)表示使得从\(a\)的这个位置往后和\(a_{i\sim n}\)的前缀匹配最长长度为\(j\)的位置数,\(buc2\)类似。可是我们想要的是使得从\(a\)的这个位置往后和\(a_{i\sim n}\)的前缀匹配最长长度\({\ge j}\)的位置数,也就是使得从\(a\)的这个位置往后和\(a_{i\sim n}\)的前缀能够匹配\(j\)这么长的位置数。于是我们可以从\(j=n-i+1\)到\(j=1\)从大到小枚举即将被check的\(a_{i\sim n}\)的前缀的长度\(j\),每次若\(buc1_j=buc2_j=1\),则check成功,更新答案\(ans=\max(ans,j)\),然后令\(buc1_{j-1}=buc1_{j}+buc1_{j-1},buc2_{j-1}=buc2_{j}+buc2_{j-1}\)即可。考虑为什么这么从大到小递推是对的:首先\(buc1_{n-i+1},buc2_{n-i+1}\)本来就有我们想要的意思。每次更新\(buc1_{j-1},buc2_{j-1}\)都会把它们变成我们想要的意思下的值(感性理解理解),于是每到一个\(j\),\(buc1_j,buc2_j\)都会是我们想要的意思咯。(想一想就会发现,上述那个递推就是\([1,buc1_j/buc2_j]\)区间\(+1\)的差分。当然如果想不到差分的话,线段树或树状数组是比较容易想的,但是带\(\log\),过不掉。。。)

这样复杂度就是\(\mathrm O(n(n+m))\),侥幸过。

下面考虑哈希怎么做。很显然是做不了的。。。最快也就是按上述方法,用哈希+二分求\(z\)数组,但数据范围不友好啊QWQ

对了,数据不清空,爆零两行泪。每枚举一个\(a\)的后缀时,都要清空\(2\)个桶!!!

下面上代码:

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=5000,M=5000;
int n,m,s;//|a|,|b|,|c|
char a[N+5],b[M+5],c[2*N+M+5]/*a的后缀+'!'+a+'@'+b*/;
int z[2*N+M+1];//z数组
void z_init(){//Z算法
int zl=0,zr=0;
for(int i=2;i<=s;i++)
if(zr<i){
z[i]=0;
while(i+z[i]<=s&&c[i+z[i]]==c[1+z[i]])z[i]++;
if(z[i])zl=i,zr=i+z[i]-1;
}
else if(i+z[i-zl+1]<=zr)z[i]=z[i-zl+1];
else{
z[i]=zr-i+1;
while(i+z[i]<=s&&c[i+z[i]]==c[1+z[i]])z[i]++;
zl=i;zr=i+z[i]-1;
}
// cout<<"z";for(int i=2;i<=s;i++)cout<<z[i];puts("");
}
int buc1[N+1],buc2[N+1];//2个桶
int main(){
cin>>a+1>>b+1;
n=strlen(a+1);m=strlen(b+1);
int ans=inf;
for(int i=1;i<=n;i++){//枚举后缀的左端点
s=0;
for(int j=i;j<=n;j++)c[++s]=a[j];
c[++s]='!';
for(int j=1;j<=n;j++)c[++s]=a[j];
c[++s]='@';
for(int j=1;j<=m;j++)c[++s]=b[j];
c[s+1]=0;
//上面都在造c
// cout<<c+1<<"\n";
z_init();
memset(buc1,0,sizeof(buc1));memset(buc2,0,sizeof(buc2));//数据不清空,爆零两行泪
// for(int j=n-i+3;j<=2*n-i+2;j++)cout<<c[j];cout<<" ";for(int j=2*n-i+4;j<=s;j++)cout<<c[j];puts("");
for(int j=n-i+3;j<=2*n-i+2;j++)buc1[z[j]]++;//装到桶里面
for(int j=2*n-i+4;j<=s;j++)buc2[z[j]]++;//同上
for(int j=n-i+1;j;j--){//枚举后缀的前缀的长度
// printf("buc1[%d]=%d buc2[%d]=%d\n",j,buc1[j],j,buc2[j]);
if(buc1[j]==1&&buc2[j]==1)ans=min(ans,j);//如果各出现恰好1次,则更新答案
buc1[j-1]+=buc1[j];buc2[j-1]+=buc2[j];//将buc1[j-1],buc2[j-1]变为我们想要的意思
}
// puts("");
// cout<<"ans="<<ans<<"\n";
}
printf("%d",ans<inf?ans:-1);
return 0;
}

CodeForces 427D Match & Catch的更多相关文章

  1. codeforces 427D Match & Catch(后缀数组,字符串)

    题目 参考:http://blog.csdn.net/xiefubao/article/details/24934617 题意:给两个字符串,求一个最短的子串.使得这个子串在两个字符串中出现的次数都等 ...

  2. Codeforces 427D Match &amp; Catch(后缀自动机)

    [题目链接] http://codeforces.com/problemset/problem/427/D [题目大意] 给出一个两个字符串,求出最短且在两个字符串中唯一的公共子串. [题解] 以原字 ...

  3. CF 427D Match &amp; Catch 求最短唯一连续LCS

    题目来源:CF 427D Match & Catch 题意:给出2个字符串 求最短的连续的公共字符串 而且该字符串在原串中仅仅出现一次 思路:把2个字符串合并起来求height 后缀数组hei ...

  4. Match & Catch CodeForces - 427D 后缀自动机水题

    题意: 给出两个字符串a,b,求一个字符串,这个字符串是a和b的子串, 且只在a,b中出现一次,要求输出这个字符串的最小长度. 题解: 将a串放入后缀自动机中,然后记录一下每个节点对应的子串出现的次数 ...

  5. Match & Catch

    Codeforces Round #244 (Div. 2) D:http://codeforces.com/contest/427/problem/D 题意:给你两个串,让你找一个最小的串,并且这个 ...

  6. CF #244 D. Match & Catch 后缀数组

    题目链接:http://codeforces.com/problemset/problem/427/D 大意是寻找两个字符串中最短的公共子串,要求子串在两个串中都是唯一的. 造一个S#T的串,做后缀数 ...

  7. D. Match & Catch 后缀自动机 || 广义后缀自动机

    http://codeforces.com/contest/427/problem/D 题目是找出两个串的最短公共子串,并且在两个串中出现的次数只能是1次. 正解好像是dp啥的,但是用sam可以方便很 ...

  8. Codeforces C Match Points(二分贪心)

    题目描述: Match Points time limit per test 2 seconds memory limit per test 256 mega bytes input standard ...

  9. CodeForces-427D:Match & Catch (后缀自动机)

    Police headquarter is monitoring signal on different frequency levels. They have got two suspiciousl ...

随机推荐

  1. C# Linq 常用查询操作符

    限定操作: 1. All:用来确定是否序列中的所有元素都满足条件 2. Any:用来确定序列是否包含任何元素,有参方式用来确定序列中是否有元素满足条件 3. Contains:方法用来确定序列是否包含 ...

  2. Greenplum+mybatis问题解析

    1. 问题描述 同事团队在使用springboot+mybatis+Greenplum时,发现通过mybatis数据查询正常,但是执行insert和update执行会报"Cause: org ...

  3. WebRTC:数据传输相关协议简介

    对网络协议来说,需要做的通常就两件事情:1.建立连接,2.传输数据,WebRTC也不例外. 假设WebRTC应用的两端已经建立了连接,那么,剩下就是如何传输数据的问题了. WebRTC同时支持传输音视 ...

  4. RabbitMQ(一):RabbitMQ快速入门

    RabbitMQ是目前非常热门的一款消息中间件,不管是互联网大厂还是中小企业都在大量使用.作为一名合格的开发者,有必要对RabbitMQ有所了解,本文是RabbitMQ快速入门文章. RabbitMQ ...

  5. .md 文件格式

    # .md 文件怎么编写 > 整理一套常用操作,自己来使用 > ## 标题 >> 写法: \# 这是一个一级标题 \## 这是一个二级标题 \### 这是一个三级标题 \### ...

  6. Appium+python自动化(二十二)- 三个臭皮匠顶个诸葛亮-控件坐标获取(超详解)

    简介 有些小伙伴或者是童鞋可能会好奇会问上一篇中的那个monkey脚本里的坐标点是如何获取的,不是自己随便蒙的猜的,或者是自己用目光或者是尺子量出来的吧,答案当然是:NO.获取控件坐标点的方式这里宏哥 ...

  7. [leetcode] 542. 01 Matrix (Medium)

    给予一个矩阵,矩阵有1有0,计算每一个1到0需要走几步,只能走上下左右. 解法一: 利用dp,从左上角遍历一遍,再从右下角遍历一遍,dp存储当前位置到0的最短距离. 十分粗心的搞错了col和row,改 ...

  8. hdu6383 p1m2(二分答案)

    p1m2 题目传送门 解题思路 因为x都是非负数,且每一次操作其实就是把总和减少了1,所以可以得出最后都可以到达稳定.最后稳定的数的下界是0,最大也不会超过其初始数的最大值,所以可以用二分答案来求解. ...

  9. 转 java - 如何判断单链表有环

    转自 https://blog.csdn.net/u010983881/article/details/78896293 1.穷举遍历 首先从头节点开始,依次遍历单链表的每一个节点.每遍历到一个新节点 ...

  10. Java_转换流和缓冲流

    今日内容介绍 转换流 缓冲流 1 转换流 在学习字符流(FileReader.FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamR ...