[CSP-S模拟测试]:字符消除2(hash+KMP)
题目背景
生牛哥终于打通了“字符消除”,可是他又被它的续集难倒了。
题目传送门(内部题52)
输入格式
第一行$n$表示数据组书。
接下来每行一个字符串。(只包含大写字母)
输出格式
每组数据输出一个$01$串。
样例
样例输入:
3
YDYYDY
JRYJREJRYJR
YDYAKYDY
样例输出:
010010
01001101001
01000010
数据范围与提示
题解
为方便,我们设串长为$S$。
首先,来解释一下什么是可行$t$。
对于样例中的第一个串$"YDYYDY"$,我看它比较帅,所以就拿它举例。
它的可行$t$集合为$3$和$6$,为什么呢?
对于一个长度为$3$的环,我们可以这么填:
这时,我们填成了一圈,然后我们去填第二圈:
刚好填完,所以它是一个可行$t$,长度为$6$时同理,可是当长度为$4$当我们填到第五个$D$时发现那个位置已经填了$Y$,所以就不可行了。
题意解释完了,我们现在开始考虑这道题应该怎么办?
先来考虑如何求出可行$t$的集合。
我们发现,如果这个串有一个长度为$len$的公共前后缀,那么其中一个可行$t$就是$len-1$,来解释一下:
对于一个公共前后缀,如下图:
橙色区域是公共前后缀,绿色区域是串,显然$a\sim b$点即为长度$len$,那么当我们处理到$c$点时完成了一个环,但是接下来我们要填的$c\sim d$这部分和$a\sim b$是一样的,所以这个$n-len$就是一个长度$t$。
对于$t$的集合,我们可以通过$hash$或者是$KMP$在$\Theta(S)$的时间复杂度内求出。
现在再来考虑如何生成$01$串,显然枚举是不可能的。
设$a$数组表示所有的公共前后缀集合(其实就是$KMP$中的$next$数组),注意不是可行$t$,那么这时候分两种情况:
$\alpha.a[k]\times 2\geqslant a[k+1]$:这时候我们就将$01$串后面接上一段长度为$a[k+1]-a[k]$的后缀即可。
$\beta.a[k]\times 2<a[k+1]$:考虑$next$数组的含义,为了保证解最优,我们一定是先将整个串复制一遍放在最后,然后在中间先填满$0$,如果不行的话将最后一个$0$换成$1$即可,至于如何判断,只能暴力了,但是均摊时间复杂度还是$\Theta(S)$的。
这样我们就完美的解决了这道题。
时间复杂度:$\Theta(\sum S)$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h>
using namespace std;
int n;
char ch[200001];
int b[200001],que[200001],nxt[200001],p;
unsigned long long a[200001],mod[200001];
void pre_work()
{
memset(nxt,0,sizeof(nxt));
memset(b,0,sizeof(b));
p=que[0]=0;
}
void KMP(int l,int r)
{
for(int i=l+1;i<=r;i++)
{
while(p&&b[i]!=b[p+1])p=nxt[p];
if(b[i]==b[p+1])p++;
nxt[i]=p;
}
}
int main()
{
int T;scanf("%d",&T);
mod[1]=1;
for(int i=2;i<=200000;i++)mod[i]=mod[i-1]*131;
while(T--)
{
scanf("%s",ch+1);
pre_work();
n=strlen(ch+1);
a[1]=ch[1]-'A'+1;
for(int i=2;i<=n;i++)
a[i]=a[i-1]*131+ch[i]-'A'+1;
for(int i=0;i<=n;i++)
if(a[i+1]==a[n]-a[n-i-1]*mod[i+2])que[++que[0]]=i+1;
if(que[1]>1)b[que[1]]=1;
KMP(1,que[1]);
for(int i=2;i<=que[0];i++)
{
if(que[i]<=que[i-1]<<1)
{
for(int j=que[i-1]+1;j<=que[i];j++)
b[j]=b[j+que[i-1]-que[i]];
KMP(que[i-1],que[i]);
}
else
{
KMP(que[i-1],que[i]-que[i-1]-1);
int now=p,zero=1,len=que[i]-que[i-1];
while(now)
{
if(!b[now+1]&&!(len%(len-now-1))){b[len]=1;break;}
now=nxt[now];
}
if(!b[now+1]&&!(len%(len-now-1)))b[len]=1;
KMP(len-1,len);
nxt[len]=p;
len=que[i]-que[i-1];
for(int j=1;j<=que[i-1];j++)b[len+j]=b[j];
KMP(len,len+que[i-1]);
}
}
for(int i=1;i<=n;i++)printf("%d",b[i]);
puts("");
}
return 0;
}
rp++
原文地址:https://www.cnblogs.com/wzc521/p/11573019.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Qt音视频开发34-Onvif时间设置
- 网络工程师提高篇 | 路由重发布你了解多少?从原理到配置,瑞哥带你学习一波!
- 短视频APP开发,简单计时功能
- LeetCode | 94.二叉树的中序遍历
- Druid 的整合
- LeetCode | 104.二叉树的最大深度
- Flutter 目录结构和项目资源
- iOS音视频接入- TRTC互动直播
- 【一天一大 lee】查找常用字符 (难度:简单) - Day20201014
- 金九银十准备换场地?对标腾讯T3的Android高级工程师面试大纲及时雨来了
- 【一天一大 lee】两两交换链表中的节点 (难度:中等) - Day20201013
- 【一天一大 lee】二叉搜索树的最小绝对差 (难度:简单) - Day20201012
- 有奖互动 | 腾讯云开发者社区 3 周年庆,我过生日,送你们礼物 ~
- 【一天一大 lee】分割等和子集 (难度:中等) - Day20201011
- 【一天一大 lee】寻找两个正序数组的中位数 (难度:困难) - Day20201003