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初始化 ...
随机推荐
- SQL Server 枚举异或运算后值存入数据库,读取符合条件的值
有枚举如下: [Flags] public enum Color { Red = , Green = , Blue = , White = } 定义三个枚举变量,并将值存入数据库: Color col ...
- PHP MySQL Delete删除数据库中的数据
PHP MySQL Delete DELETE 语句用于从数据库表中删除行. 删除数据库中的数据 DELETE FROM 语句用于从数据库表中删除记录. 语法 DELETE FROM table_na ...
- Python File fileno() 方法
概述 fileno() 方法返回一个整型的文件描述符(file descriptor FD 整型),可用于底层操作系统的 I/O 操作.高佣联盟 www.cgewang.com 语法 fileno() ...
- PDOStatement::errorInfo
PDOStatement::errorInfo — 获取跟上一次语句句柄操作相关的扩展错误信息(PHP 5 >= 5.1.0, PECL pdo >= 0.1.0) 说明 语法 array ...
- 非常适合小白的 Asyncio 教程
非常适合小白的 Asyncio 教程 原文:https://segmentfault.com/a/1190000008814676 所谓「异步 IO」,就是你发起一个 IO 操作,却不用等它结束, ...
- ipa包如何打包?ios打包ipa的四种方法分享
今天带来的内容是ios打包ipa的四种方法.总结一下,目前.app包转为.ipa包的方法有以下几种,下面一起来看看吧! 1.Apple推荐的方式,即实用xcode的archive功能 Xco ...
- 简单python爬虫编写,Python采集妹子图!
疫情期间在家闲来无事,每天打游戏荒废了一段时间.我觉得自己不能在这么颓废下去,就立马起身写了一点python代码(本人只是python新手). 很多人学习python,不知道从何学起.很多人学习pyt ...
- git使用-标签管理
1.查看所有的标签 git tag 2.创建标签 git tag name 3.指定提交标签的信息 git tag -a name -m "comment" 4.删除标签 git ...
- Linux系统安装Nginx(Centos7)
Nginx是一款轻量级的网页服务器.反向代理服务器.它最常的用途是提供反向代理服务,还可以做负载均衡.相较于Apache.lighttpd具有占有内存少,稳定性高等优势.服务端很多场景都需要使用,这篇 ...
- 他们都说JVM能实际使用的内存比-Xmx指定的少?这是为什么呢
这确实是个挺奇怪的问题,特别是当最常出现的几种解释理由都被排除后,看来JVM并没有耍一些明显的小花招: -Xmx和-Xms是相等的,因此检测结果并不会因为堆内存增加而在运行时有所变化. 通过关闭自适应 ...