给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 3 个字符,结果可能有多少种不同的字符串?

输入格式:

输入在一行中给出全部由小写英文字母组成的、长度在区间 [4, 1] 内的字符串。

输出格式:

在一行中输出至多删掉其中 3 个字符后不同字符串的个数。

输入样例:

ababcc

输出样例:

25

提示:

删掉 0 个字符得到 "ababcc"。

删掉 1 个字符得到 "babcc", "aabcc", "abbcc", "abacc" 和 "ababc"。

删掉 2 个字符得到 "abcc", "bbcc", "bacc", "babc", "aacc", "aabc", "abbc", "abac" 和 "abab"。

删掉 3 个字符得到 "abc", "bcc", "acc", "bbc", "bac", "bab", "aac", "aab", "abb" 和 "aba"。

解法:

前置技能:求一个序列中所有的不同子序列个数。

eg:FZU - 2129

设dp[i]为序列a的前i个元素所组成的不同子序列个数,则有状态转移方程:$dp[i]=\left\{\begin{matrix}\begin{aligned}&2dp[i-1]+1,pre[a[i]]=-1\\&2dp[i-1]-dp[pre[a[i]]-1],pre[a[i]]\neq -1\end{aligned}\end{matrix}\right.$

其中pre[a[i]]表示a[i]前面第一个和a[i]相同的元素的下标。

解释:第i个元素a[i]有两种选择:选或不选。

若不选a[i],则dp[i]继承dp[i-1]的全部子序列,因此有dp[i]+=dp[i-1]。

若选a[i],则dp[i]在dp[i-1]的全部子序列的尾部填加了个元素a[i],因此仍有dp[i]+=dp[i-1]。但这样会有很多重复的序列,因此要去重,即去掉前面和a[i]相同的元素之前的序列(因为它们加上a[i]形成的序列已经被算过了),因此有dp[i]-=dp[pre[a[i]]-1]。特别地,如果a[i]前面没有与a[i]相同的元素,那么没有重复的序列,并且a[i]自己单独形成一个新序列,此时dp[i]++。

 #include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
typedef double db;
const int N=1e6+,mod=1e9+;
int a[N],n,dp[N],pre[N];
int main() {
while(scanf("%d",&n)==) {
memset(pre,-,sizeof pre);
for(int i=; i<=n; ++i)scanf("%d",&a[i]);
dp[]=;
for(int i=; i<=n; ++i) {
dp[i]=(ll)dp[i-]*%mod;
if(~pre[a[i]])dp[i]=((ll)dp[i]-dp[pre[a[i]]-])%mod;
else dp[i]=(dp[i]+)%mod;
pre[a[i]]=i;
}
printf("%d\n",(dp[n]+mod)%mod);
}
return ;
}

回到正题,此题是上题的升级版,等价于求一个长度为n的序列中长度为n,n-1,n-2,n-3的不同子序列个数之和。

基本思路是一致的,只需要在上述代码的基础上稍作改动即可。

设dp[i][j]为前i个元素删了j个元素所形成的子序列个数,则有$dp[i][j]=\left\{\begin{matrix}\begin{aligned}&dp[i-1][j-1]+dp[i-1][j],pre[a[i]]=-1,j\neq i-1\\&dp[i-1][j-1]+dp[i-1][j]+1,pre[a[i]]=-1,j=i-1\\&dp[i-1][j-1]+dp[i-1][j]-dp[pre[a[i]]-1][j-(i-pre[a[i]])],pre[a[i]]\neq -1\end{aligned}\end{matrix}\right.$

推导过程类似,注意j的变化即可。

 #include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
typedef double db;
const int N=1e6+;
char a[N];
int n,pre[];
ll dp[N][];
int main() {
memset(pre,-,sizeof pre);
scanf("%s",a+),n=strlen(a+);
for(int i=; i<=n; ++i) {
for(int j=; j<=; ++j) {
if(j>)dp[i][j]+=dp[i-][j-];
dp[i][j]+=dp[i-][j];
if(~pre[a[i]]&&j>=i-pre[a[i]])dp[i][j]-=dp[pre[a[i]]-][j-(i-pre[a[i]])];
else if(i==j+)dp[i][j]++;
}
pre[a[i]]=i;
}
printf("%lld\n",dp[n][]+dp[n][]+dp[n][]+dp[n][]);
return ;
}

还有另一种解法是利用序列自动机,很简单,设go[i][j]为第i个元素后第一个元素j出现的位置,先用类似dp的方式建立自动机,则问题转化成了一个DAG上的dp问题。

但是由于序列自动机空间消耗较大,直接dfs可能会爆内存,比如这样:

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N=1e6+,M=;
char s[N];
int n,go[N][M],dp[N][];
void build() {
memset(go[n],,sizeof go[n]);
for(int i=n-; i>=; --i)memcpy(go[i],go[i+],sizeof go[i]),go[i][s[i]-'a']=i+;
}
int dfs(int u,int k) {
if(k>)return ;
int& ret=dp[u][k];
if(~ret)return ret;
ret=(k+(n-u)<=);
for(int i=; i<M; ++i)if(go[u][i])ret+=dfs(go[u][i],k+go[u][i]-u-);
return ret;
}
int main() {
scanf("%s",s),n=strlen(s);
build();
memset(dp,-,sizeof dp);
printf("%d\n",dfs(,));
return ;
}

解决方法是自底而上,一遍dp一遍更新go数组,成功AC:

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N=1e6+,M=;
char s[N];
int n,go[M];
ll dp[N][];
int main() {
scanf("%s",s),n=strlen(s);
dp[n][]=dp[n][]=dp[n][]=dp[n][]=;
for(int i=n-; i>=; --i) {
go[s[i]-'a']=i+;
for(int j=; j<=; ++j) {
dp[i][j]=(j+(n-i)<=);
for(int k=; k<M; ++k)if(go[k]&&j+go[k]-i-<=)dp[i][j]+=dp[go[k]][j+go[k]-i-];
}
}
printf("%lld\n",dp[][]);
return ;
}

虽然序列自动机的功能比较强大,但时间和空间的消耗都与元素集合的大小有关,因此当元素集合过大的时候,可能就并不吃香了~~

PTA L3-020 至多删三个字符 (序列dp/序列自动机)的更多相关文章

  1. pta l3-20(至多删三个字符)

    题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805046946938880 题意:给定一个长度<=106 ...

  2. L3-020 至多删三个字符 (30 分)(DP)

    题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805046946938880 学习地址: 2018CCCC-L3 ...

  3. L3-020 至多删三个字符 (30 分)

    给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 3 个字符,结果可能有多少种不同的字符串? 输入格式: 输入在一行中给出全部由小写英文字母组成的.长度在区间 [4, 1] 内的字符串. 输 ...

  4. PAT L3-020 至多删三个字符

    https://pintia.cn/problem-sets/994805046380707840/problems/994805046946938880 给定一个全部由小写英文字母组成的字符串,允许 ...

  5. L3-020 至多删三个字符 (30 分) 线性dp

    给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 3 个字符,结果可能有多少种不同的字符串? 输入格式: 输入在一行中给出全部由小写英文字母组成的.长度在区间 [4, 1] 内的字符串. 输 ...

  6. PTA 团体程序设计天梯赛 L3-020 至多删三个字符

    $f[i][j]$表示到第$i$个字符,已经删去了$j$个字符的方案数. 显然的转移: $f[i][j] = f[i - 1][j] + f[i - 1][j - 1]$ 但是这样会有重复,我们考虑什 ...

  7. [leetcode]680. Valid Palindrome II有效回文II(可至多删一原字符)

    Given a non-empty string s, you may delete at most one character. Judge whether you can make it a pa ...

  8. PTA 最多删除3个字符(DP) - 30分

    给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 3 个字符,结果可能有多少种不同的字符串? 输入格式: 输入在一行中给出全部由小写英文字母组成的.长度在区间 [4, 1] 内的字符串. 输 ...

  9. windows自带记事本导致文本文件(UTF-8编码)开头三个字符乱码问题

    在windows平台下,使用系统的记事本以UTF-8编码格式存储了一个文本文件,但是由于Microsoft开发记事本的团队使用了一个非常怪异的行为来保存UTF-8编码的文件,它们自作聪明地在每个文件开 ...

随机推荐

  1. 学习地址 hadoop生态圈

    http://my.oschina.net/leejun2005/blog/140462 http://www.codelast.com/?p=3621&cpage=1#comment-361 ...

  2. $微信小程序开发实践点滴——Bmob常用API的使用

    Bmob后端云官网:http://www.bmob.cn/ Bmob后端云微信小程序开发文档:http://docs.bmob.cn/data/wechatApp/b_developdoc/doc/i ...

  3. Java并发之CyclicBarria的使用

    Java并发之CyclicBarria的使用 一.简介 笔者在写CountDownLatch这个类的时候,看到了博客园上的<浅析Java中CountDownLatch用法>这篇博文,为博主 ...

  4. hadoop08---读写锁

    ReentrantLock 直接使用lock接口的话,我们需要实现很多方法,不太方便,ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法,Reen ...

  5. Python面试题之Super函数

    这是个高大上的函数,在python装13手册里面介绍过多使用可显得自己是高手 23333. 但其实他还是很重要的. 简单说, super函数是调用下一个父类(超类)并返回该父类实例的方法. 这里的下一 ...

  6. 第九节课-CNN架构

    2017-08-21 这次的课程比较偏向实际的运用,介绍了当前几种比较主流的CNN网络: 主要是AlexNet,GoogleNet, VGG, ResNet 需要把课后习题做了才能更好的理解.

  7. Java web应用中的常见字符编码问题的解决方法

    以下是 Java Web应用的常见编码问题 1. html页面的编码 在web应用中,通常浏览器会根据http header: Content-type的值来决定用什么encoding, 比如遇到Co ...

  8. 分布式技术 webapi

    webapi可以返回json.xml类型的数据,对于数据的增.删.改.成,提供对应的资源操作,按照请求的类型进行相应的处理,主要包括 Get(查).Post(增).Put(改).Delete(删),这 ...

  9. struts2——文件下载(简单的功能)

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  10. nginx 中location和root、alias

    nginx指定文件路径有两种方式root和alias,这两者的用法区别 root与alias主要区别在于nginx如何解释location后面的uri,这会使两者分别以不同的方式将请求映射到服务器文件 ...