不能相邻的字符对

这是一篇关于如何利用动态规划(DP)解决字符对不能相邻问题的博客。题目中,N≤100000,M≤400,通过分析问题的无后效性,作者提出了初始的O(n^2)复杂度解决方案,然后通过转换思路,将复杂度降低到O(n),从而提高了效率。文章包含关键的转移方程和优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

小 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值