CF1203D2 Remove the Substring (hard version) 题解
这题初赛让我白给了6分,于是我决定回来解决一下它。
说实话,看原题题面和看CCF代码真是两种完全不同的感受……
------------
思路分析:
把$s$串删去一部分之后,会把$s$串分成两部分,当然其中一部分有可能为空。$t$串作为$s$串的字串,在删去一部分之后也会被分为两部分。因此我们可以枚举$t$串被分开的位置,然后进行计算。
设$t$串在$t[p]$和$t[p+1]$之间被分开,$s$串在$s[i]$和$s[i+1]$之间被分开。因为要使答案最大,因此我们要让$s$串的左半部分包含且仅包含一个$t$串的左半部分,右半部分也是一样。
因此,按照CCF的讲法,设$s$串和$t$串的长度分别为$slen$和$tlen$,我们可以设$head[i]=p$表示$s[0...i]$包含且仅包含一个子串为$t[0...p]$,设$back[i]=p$表示$s[i...slen-1]$包含且仅包含一个字串为$t[p...tlen-1]$。
如何递推$head$和$back$数组?很显然了,用两个指针分别指向$s$串和$t$串,表示当前位置,相同即匹配成功。
因为指针初值需要设置为0,因此以下代码将$s$串和$t$串都整体后移了一位。
p=;
for(int i=;i<s.size();i++)
{
head[i]=head[i-];
if(s[i]==t[p])
head[i]=p++;
}
p=t.size()-;back[s.size()]=t.size();
for(int i=s.size()-;i;i--)
{
back[i]=back[i+];
if(s[i]==t[p])
back[i]=p--;
}
递推出$head$和$back$数组后,枚举断点计算答案就行了。用两个指针$i,j$分别指向删除的部分两端。为了让答案最大,应该找到满足$head[i]=p$的最小的$i$,以及满足$back[j]=p+1$的最大的$j$,这两个步骤用两个while循环就可以轻松搞定了。有一个需要注意的点,当被分开的两部分其中一部分包含整个$t$串时,另一部分取空串是最优的,这个需要特判一下。答案即为$j-i-1$。计算答案时也要特判不合法的情况。各个指针的初值也需要注意。
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
const int N=1e6;
int p=,ans;
int head[N],back[N];
string s,t;
int main()
{
cin>>s>>t;s='.'+s,t='.'+t;//后移一位
for(int i=;i<s.size();i++)
{
head[i]=head[i-];
if(s[i]==t[p])
head[i]=p++;
}
p=t.size()-;back[s.size()]=t.size();
for(int i=s.size()-;i;i--)
{
back[i]=back[i+];
if(s[i]==t[p])
back[i]=p--;
}
p=;
for(int i=,j=;i<s.size(),j<s.size(),p<t.size();p++)
{
while(head[i]<p && i<s.size())
i++;
while(back[j+]<=p+ && j<s.size())
j++;//找到最优的i和j
if(p==t.size()-)
j=s.size();//贪心地让另一部分为空串
if(i<s.size() && j<=s.size())//合法才更新答案
ans=max(ans,j-i-);
}
printf("%d",ans);
return ;
}
因为指针$i,j$一定是递增的,因此时间复杂度$O(n)$。
------------
既然是初赛原题,那么再来看看CCF的代码:(实测比我的代码快15ms...不过我的代码有的地方常数的确比较大)
#include<iostream>
#include<string>
using namespace std;
const int maxl=1e6;//改了一下数组大小,可以过困难版
string s,t;
int pre[maxl],suf[maxl];//分别相当于head和back数组 int main(){
cin>>s>>t;
int slen=s.length(),tlen=t.length();
for(int i=,j=;i<slen;++i){
if(j<tlen && s[i]==t[j]) ++j;
pre[i]=j;
}
for(int i=slen-,j=tlen-;i>=;--i){
if(j>= && s[i]==t[j]) --j;
suf[i]=j;
}
suf[slen]=tlen-;//递推基本相同,表示略有区别
int ans=;
for(int i=,j=,tmp=;i<=slen;++i){
while(j<=slen && tmp>=suf[j]+) ++j;
ans=max(ans,j-i-);
tmp=pre[i];
}
cout<<ans<<endl;
return ;
}
可以发现思路实际上大体相同,但是计算答案时略有差别。
分析一下这个计算答案的过程,枚举$s$的断点。可以发现,对于当前的循环,$tmp=pre[i-1]$,即$t[0...tmp-1]$是$s[0...i-1]$的字串,那么接下来为了使答案最大,应该找到满足$t[tmp...tlen-1]$是$s[j...slen-1]$的$j$。而这份代码中的while循环找到的$j$应该会比我们要找的$j$大1,因此要删除的部分就是$s[i...j-2]$,长度$j-i-1$。
然后分析一下题目:(理解了题意之后感觉想错都难qwq)
1.
Q:程序输出时,suf数组满足:对于任意$0\leq i\leq slen$,$suf[i]\leq suf[i+1]$。
A:T,显然$suf$数组是递增的。
2.
Q:当$t$是$s$的子序列时,输出一定不为0。
A:F,反例样例3。
3.
Q:程序运行到第23行时,“j-i-1”一定不小于0。
A:F,在本题中的确不会出现这种情况,但初赛题目中可没保证$t$是$s$的子串,$t$不是$s$的字串时就会出现这种情况。
4.
Q:当$t$是$s$的子序列时,$pre$数组和$suf$数组满足:对于任意$0\leq i<slen$,$pre[i]>suf[i+1]+1$。
A:F,反例样例1。
5.
Q:若$tlen=10$,输出为0,则$slen$最小为( )。
A:输出为0说明$t$不是$s$的子序列或者$s=t$,显然前者可以使答案更小,即$t$越短越好。由于cin不能输入空串,因此最短只能是1。
6.
Q:若$tlen=10$,输出为2,则$slen$最小为( )。
A:$s$最多删去两个字符使$t$仍是$s$的字串,显然$t$串最短长度为12。
最后祝大家CSP-J/S 2019rp++。
CF1203D2 Remove the Substring (hard version) 题解的更多相关文章
- D2. Remove the Substring (hard version)(思维 )
D2. Remove the Substring (hard version) time limit per test 2 seconds memory limit per test 256 mega ...
- CF #579 (Div. 3) D1.Remove the Substring (easy version)
D1.Remove the Substring (easy version) time limit per test2 seconds memory limit per test256 megabyt ...
- D2. Remove the Substring (hard version)
D2. Remove the Substring (hard version) 给字符串s,t,保证t为s的子序列,求s删掉最长多长的子串,满足t仍为s的子序列 记录t中每个字母在s中出现的最右的位置 ...
- Codeforces 1196D2 RGB Substring (Hard version) 题解
题面 \(q\) 个询问,每个询问给出一个字符串 \(s\),要你在 \(s\) 中用最小替换得到无穷字符串 RGBRGBRGB... 的长度为定值 \(k\) 的子串. 题解 一眼看过去可能是编辑距 ...
- Codeforces Round #579 (Div. 3) D2. Remove the Substring (hard version) (思维,贪心)
题意:给你一个模式串\(t\),现在要在主串\(s\)中删除多个子串,使得得到的\(s\)的子序列依然包含\(t\),问能删除的最长子串长度. 题解:首先,我们不难想到,我们可以选择\(s\)头部到最 ...
- 双指针(最大删除子串)Codeforces Round #579 (Div. 3)--Remove the Substring (hard version)
题目链接:https://codeforces.com/contest/1203/problem/D2 题意: 给你S串.T串,问你最长删除多长的子串使得S串里仍然有T的子序列. 思路: 想了好久,先 ...
- Codeforces - 1203D2 - Remove the Substring (hard version) - 双指针
https://codeforces.com/contest/1203/problem/D2 上次学了双指针求两个字符串之间的是否t是s的子序列.但其实这个双指针可以求出的是s的前i个位置中匹配t的最 ...
- Remove the Substring
D2. Remove the Substring (hard version) 思路:其实就是贪心吧,先从前往后找,找到 t 可在 s 中存在的最小位置 (pre),再从后往前找,找到 t 可在 s ...
- Codeforces Round #575 (Div. 3) D2. RGB Substring (hard version) 【递推】
一.题目 D2. RGB Substring (hard version) 二.分析 思路一开始就想的对的,但是,用memset给数组初始化为0超时了!超时了! 然后我按照题解改了个vector初始化 ...
随机推荐
- PHP md5_file() 函数
实例 计算文本文件 "test.txt" 的 MD5 散列: <?php高佣联盟 www.cgewang.com$filename = "test.txt" ...
- ElasticSearch 基础入门 and 操作索引 and 操作文档
基本概念 索引: 类似于MySQL的表.索引的结构为全文搜索作准备,不存储原始的数据. 索引可以做分布式.每一个索引有一个或者多个分片 shard.每一个分片可以有多个副本 replica. 文档: ...
- HTML与XHTML之间的区别
HTML与XHTML之间的差别,粗略可以分为两大类比较:一个是功能上的差别,另外是书写习惯的差别.关于功能上的差别,主要是XHTML可兼容各大浏览器.手机以及PDA,并且浏览器也能快速正确地编译网页. ...
- [转]Spring Security架构
作者:before31原文:https://my.oschina.net/xuezi/blog/3126351 本指南是Spring Security的入门,它提供了对该框架的设计和基本构建的见解.我 ...
- 学Java必看!零基础小白再也不用退缩了
程序员们!请往这儿看 对于JAVA的学习,可能你还会有许多的顾虑 不要担心 接着往下看吧 学Java前 一.数学差,英语也不好是不是学不好Java? 答案是:是~ 因为你在问这个问题的时候说明你对自己 ...
- 树状图展示终端目录内容-tree
以树状图列出目录的内容,让你一目了然 执行 tree 指令,它会列出指定目录下的所有文件,包括子目录里的文件. 安装 我们通过包管理工具可以方便的安装它 mac - brew install tree ...
- 还在纠结学什么编程语言吗?Python可能会“教”你做人
这几年为什么Python在中国就火起来了? Python这个东西国,大概是从2017年末开始,突然就火了起来的.此前,对于Python,乃至编程,绝大多数程度上都是专业人士的话题,在普通大众层面上起不 ...
- MySQL数据库高可用方案
一.什么是高可用性: 高可用性=可靠性,它的本质就是通过技术和工具提高可靠性,尽可能长时间保持数据可用和系统运行,实现高可用性的原则,首先要消除单点故障,其次通过冗余机制实现快速恢复,还有就是实现容错 ...
- Grazing on the Run 题解
[题目大意] 大致题意就是,你的初始坐标为\(x\),你要去数轴上的\(n\)个点,问你到达所有点的时间总和最小是多少. 直接贪心肯定不行,所以考虑\(DP\) 先把坐标离散(也就是预处理两点距离\( ...
- JS学习第六天
匿名函数: 定义:function(参数列表){ 要执行的语句块: } 定义名(): 创建日期对象:Date var date=new Date(); alert(date); 不输入则是默认月,日 ...