这题初赛让我白给了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. SQL Server 枚举异或运算后值存入数据库,读取符合条件的值

    有枚举如下: [Flags] public enum Color { Red = , Green = , Blue = , White = } 定义三个枚举变量,并将值存入数据库: Color col ...

  2. PHP MySQL Delete删除数据库中的数据

    PHP MySQL Delete DELETE 语句用于从数据库表中删除行. 删除数据库中的数据 DELETE FROM 语句用于从数据库表中删除记录. 语法 DELETE FROM table_na ...

  3. Python File fileno() 方法

    概述 fileno() 方法返回一个整型的文件描述符(file descriptor FD 整型),可用于底层操作系统的 I/O 操作.高佣联盟 www.cgewang.com 语法 fileno() ...

  4. PDOStatement::errorInfo

    PDOStatement::errorInfo — 获取跟上一次语句句柄操作相关的扩展错误信息(PHP 5 >= 5.1.0, PECL pdo >= 0.1.0) 说明 语法 array ...

  5. 非常适合小白的 Asyncio 教程

      非常适合小白的 Asyncio 教程 原文:https://segmentfault.com/a/1190000008814676 所谓「异步 IO」,就是你发起一个 IO 操作,却不用等它结束, ...

  6. ipa包如何打包?ios打包ipa的四种方法分享

      今天带来的内容是ios打包ipa的四种方法.总结一下,目前.app包转为.ipa包的方法有以下几种,下面一起来看看吧!    1.Apple推荐的方式,即实用xcode的archive功能 Xco ...

  7. 简单python爬虫编写,Python采集妹子图!

    疫情期间在家闲来无事,每天打游戏荒废了一段时间.我觉得自己不能在这么颓废下去,就立马起身写了一点python代码(本人只是python新手). 很多人学习python,不知道从何学起.很多人学习pyt ...

  8. git使用-标签管理

    1.查看所有的标签 git tag 2.创建标签 git tag  name 3.指定提交标签的信息 git tag -a name -m "comment" 4.删除标签 git ...

  9. Linux系统安装Nginx(Centos7)

    Nginx是一款轻量级的网页服务器.反向代理服务器.它最常的用途是提供反向代理服务,还可以做负载均衡.相较于Apache.lighttpd具有占有内存少,稳定性高等优势.服务端很多场景都需要使用,这篇 ...

  10. 他们都说JVM能实际使用的内存比-Xmx指定的少?这是为什么呢

    这确实是个挺奇怪的问题,特别是当最常出现的几种解释理由都被排除后,看来JVM并没有耍一些明显的小花招: -Xmx和-Xms是相等的,因此检测结果并不会因为堆内存增加而在运行时有所变化. 通过关闭自适应 ...