小 W W 理 笔记
【问题 描 述】
小 W 由于购物浪费了太多时间,导致他外语课都没有好好听。为什么是外语课?不是英
语课?因为小 W 肯定会好好听英语课。由于没有好好听课, 小 W 的笔记全都记的杂乱无章,
出现了好多错误的地方。 小 W 的笔记是如此的糟糕,以至于他只记了一句例句,而且自己
还不知道是什么意思……然后在老师讲语法的时候, 小 W 又零星的记了几个字母对,老师
说:这几个字母对是绝对不能相邻的,而且相邻是不关心字母的顺序的,比如老师说,‘ab’
不能相邻,那么相同的,‘ba’也不能相邻。
现在小 W 到家了,打开了上课的笔记,然后他发现笔记有很多自相矛盾的地方:为什
么下面的不能相邻的字母对会出现在上面的例句里面呢?纠结再三, 小 W 觉得下面的东西
相对比较简单,所以记错的概率比较小……他决定在上面的例句里面擦掉几个字母,使得句
子变得合法。
但是小 W 还要把礼物送给小 M,来不及整理笔记了,就把这个艰巨的任务留给了大家,
请问大家, 小 W 最少要擦掉几个字母,才能使得上面的例句合法?
【输入格式】
第一行:一个整数 N。
第二行:一个长度为 N 的字符串,只包含小写字母,代表小 W 记下的例句。
第三行:一个整数 M,代表不能相邻的字母对的个数。
接下来 M 行:每行两个小写字母,代表不能相邻的字母对,因为小 W 太不认真了,所以可
能有重复。
【输出格式】
一行一个整数:最少要擦除的字母数。
【输入输出样例】
note.in note.out
4 1
jsoi
2
oi
mo
【 数据规模 】
对于 10%的数据,M=0
对于另外 30%的数据,N≤1000
【问题 描 述】
小 W 由于购物浪费了太多时间,导致他外语课都没有好好听。为什么是外语课?不是英
语课?因为小 W 肯定会好好听英语课。由于没有好好听课, 小 W 的笔记全都记的杂乱无章,
出现了好多错误的地方。 小 W 的笔记是如此的糟糕,以至于他只记了一句例句,而且自己
还不知道是什么意思……然后在老师讲语法的时候, 小 W 又零星的记了几个字母对,老师
说:这几个字母对是绝对不能相邻的,而且相邻是不关心字母的顺序的,比如老师说,‘ab’
不能相邻,那么相同的,‘ba’也不能相邻。
现在小 W 到家了,打开了上课的笔记,然后他发现笔记有很多自相矛盾的地方:为什
么下面的不能相邻的字母对会出现在上面的例句里面呢?纠结再三, 小 W 觉得下面的东西
相对比较简单,所以记错的概率比较小……他决定在上面的例句里面擦掉几个字母,使得句
子变得合法。
但是小 W 还要把礼物送给小 M,来不及整理笔记了,就把这个艰巨的任务留给了大家,
请问大家, 小 W 最少要擦掉几个字母,才能使得上面的例句合法?
【输入格式】
第一行:一个整数 N。
第二行:一个长度为 N 的字符串,只包含小写字母,代表小 W 记下的例句。
第三行:一个整数 M,代表不能相邻的字母对的个数。
接下来 M 行:每行两个小写字母,代表不能相邻的字母对,因为小 W 太不认真了,所以可
能有重复。
【输出格式】
一行一个整数:最少要擦除的字母数。
【输入输出样例】
note.in note.out
4 1
jsoi
2
oi
mo
【 数据规模 】
对于 10%的数据,M=0
对于另外 30%的数据,N≤1000
对于 100%的数据,N≤100000,M≤400
分析:
可以看出题目的无后效性,所以采用dp的做法。
我们定义状态f[i]表示前i个字符所需擦去的最小个数,所以有f[i]=min(f[j]+(i-j)),且j是可以与i相邻的字符。
效率O(n^2),显然不够。
我们再来看一看转移方程,由于可以与i相邻的j总共只可能是26种字符,所以我们可以维护M(j)表示最后一位保留j字符的最小擦去数,但是我们还要维护对应的j的位置,否则怎么计算(i-j)呢?仍然有两个因素限制结果,如果采取枚举一个计算另一个的做法,效率就没有降低。
如果我们不计算最小擦去数,而反过来计算最大保留数呢?
那么f[i]=max(f[j]+1),维护一个M(j),显然就可以解决问题,将复杂度降为O(n)
参考程序:
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=110000;
int f[maxn],g[maxn];
int last[30],forbid[30][30];
char b[maxn];
int n,m;
int main(){
freopen("note.in","r",stdin);
freopen("note.out","w",stdout);
scanf("%d",&n);
scanf("%s",b+1);
for (int i=1;i<=n;i++)
f[i]=b[i]-'a';
scanf("%d",&m);
while (m--){
scanf("%s",b);
forbid[b[0]-'a'][b[1]-'a']=forbid[b[1]-'a'][b[0]-'a']=1;
}
int ans=0;
for (int i=1;i<=n;i++){
g[i]=1;
for (int j=0;j<26;j++)
if (!forbid[j][f[i]])
g[i]=max(g[i],last[j]+1);
last[f[i]]=max(last[f[i]],g[i]);
ans=max(ans,g[i]);
}
printf("%d",n-ans);
return 0;
}