不能相邻的字符对

来源:互联网 发布:阿里云os5.1系统root 编辑:程序博客网 时间:2024/04/30 18:10
小 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

对于 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;}


0 0
原创粉丝点击