这题初赛让我白给了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) 题解的更多相关文章

  1. D2. Remove the Substring (hard version)(思维 )

    D2. Remove the Substring (hard version) time limit per test 2 seconds memory limit per test 256 mega ...

  2. 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 ...

  3. D2. Remove the Substring (hard version)

    D2. Remove the Substring (hard version) 给字符串s,t,保证t为s的子序列,求s删掉最长多长的子串,满足t仍为s的子序列 记录t中每个字母在s中出现的最右的位置 ...

  4. Codeforces 1196D2 RGB Substring (Hard version) 题解

    题面 \(q\) 个询问,每个询问给出一个字符串 \(s\),要你在 \(s\) 中用最小替换得到无穷字符串 RGBRGBRGB... 的长度为定值 \(k\) 的子串. 题解 一眼看过去可能是编辑距 ...

  5. Codeforces Round #579 (Div. 3) D2. Remove the Substring (hard version) (思维,贪心)

    题意:给你一个模式串\(t\),现在要在主串\(s\)中删除多个子串,使得得到的\(s\)的子序列依然包含\(t\),问能删除的最长子串长度. 题解:首先,我们不难想到,我们可以选择\(s\)头部到最 ...

  6. 双指针(最大删除子串)Codeforces Round #579 (Div. 3)--Remove the Substring (hard version)

    题目链接:https://codeforces.com/contest/1203/problem/D2 题意: 给你S串.T串,问你最长删除多长的子串使得S串里仍然有T的子序列. 思路: 想了好久,先 ...

  7. Codeforces - 1203D2 - Remove the Substring (hard version) - 双指针

    https://codeforces.com/contest/1203/problem/D2 上次学了双指针求两个字符串之间的是否t是s的子序列.但其实这个双指针可以求出的是s的前i个位置中匹配t的最 ...

  8. Remove the Substring

    D2. Remove the Substring (hard version) 思路:其实就是贪心吧,先从前往后找,找到 t 可在 s 中存在的最小位置 (pre),再从后往前找,找到 t 可在 s ...

  9. Codeforces Round #575 (Div. 3) D2. RGB Substring (hard version) 【递推】

    一.题目 D2. RGB Substring (hard version) 二.分析 思路一开始就想的对的,但是,用memset给数组初始化为0超时了!超时了! 然后我按照题解改了个vector初始化 ...

随机推荐

  1. Python 字典(Dictionary) type()方法

    Python 字典(Dictionary) type()方法 描述 Python 字典(Dictionary) type() 函数返回输入的变量类型,如果变量是字典就返回字典类型.高佣联盟 www.c ...

  2. day20:正则表达式

    单个字符的匹配 findall(正则表达式,字符串) 把符合正则表达式的字符串存在列表中返回 预定义字符集(8) \d 匹配数字 \D 匹配非数字 \w 匹配数字字母下划线 \W 匹配非数字或字母或下 ...

  3. 牛客练习赛60 D 斩杀线计算大师

    LINK:斩杀线计算大师 给出a,b,c三个值 求出 ax+by+cz=k的x,y,z的正整数解 保证一定有解. 考虑两个数的时候 ax+by=k 扩展欧几里得可以解决. 三个数的时候 一个暴力的想法 ...

  4. EF Code First数据库模型及属性约束

    1.今日完成任务 数据库实体的创建 实体属性约束的添加 实体之间关系的添加 2.核心代码 EF模型 属性约束及实体之间的关系 使用FlutAPI对模型进行修正 3.遇到的问题及解决方案 最主要的是联合 ...

  5. 【AHOI2009】中国象棋 题解(线性DP+数学)

    前言:这题主要是要会设状态,状态找对了问题迎刃而解. --------------------------- 题目描述 这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可 ...

  6. C++STL算法

    1.不变序列算法 不会修改算法所作用的容器或对象 适用于顺序容器和关联容器,时间复杂度为O(n). 2.变值算法 会修改源区间或目标区间元素的值,值被修改的那个区间,不可属于关联容器. 3.删除算法 ...

  7. C# Hello Word

    不管学习什么语言,第一个例子绝对是一个经典的HelloWorld程序那么接下来我们使用 vs studio 2019 来创建一个 HelloWorld 程序 启动vs2019选择 文件-新建-项目-新 ...

  8. ES集群部署

    1.环境准备 主机名 IP地址 CPU 内存 硬盘 gztxy-prd-es01 192.168.1.11 8 16 200 gztxy-prd-es01 192.168.1.12 8 16 200 ...

  9. 我给这个Python库打101分!

    日志在开发过程中是一种被很多程序员 不重视 ,但是却 至关重要 的一项功能. 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案 ...

  10. 教你看懂Docker和K8S!

    转载于 https://my.oschina.net/jamesview/blog/2994112 2010年,几个搞IT的年轻人,在美国旧金山成立了一家名叫“dotCloud”的公司. 这家公司主要 ...