[JZOJ4673] 【NOIP2016提高A组模拟7.20】LCS again

题目

描述

在这里插入图片描述

题目大意

给你一个字符串和字符的取值范围,问和这个字符串的最长公共子串的长度为 N − 1 N-1 N1的串的个数、


思考历程

一看就知道这是一个神仙题。
思考了一会儿,觉得AC是没有希望的了。
于是我的目标渐渐地降下来,最终,目标变成了如何才能拿分。
这似乎是一道连拿分都不容易的题目!
于是我想过各种方法,什么DP之类的,但是都没有想出来。
于是这题就愉快地没有分了。
比赛后我问同学怎么拿分的。
他说用set判重。
我恨不得喷出一口老血!


正解

题解的DP方法我还不清楚·,就先说说另一个方法。
显然要再原来的字符串中删去一个字符,然后从某个位置加入另一个字符。
不考虑重复,单纯地想一想:从某个位置删去一个字符,再到其它地方插入的方案数。
在删除这个字符之后的空隙数为 n n n,可以放的字符种类为 m m m,所以是 n m nm nm种方案。
但这当然会有重复。
将字符插入到某个位置旁边的时候,如果这个位置上的字符和它一样,那么插在左边和右边是等价的,也就是重复了。
这样算就有 n − 1 n-1 n1个重复,减去。由于先前的位置不能插入一样的字符,就再减 1 1 1
综上,方案数应该是 n ( m − 1 ) n(m-1) n(m1)
再考虑更多重复的情况。
可以将原字符串划分成许多块,每块是连续的相同的字符。
我们发现删去块中的每个字符都是等价的,所以我们只需要对于每个块加上 n ( m − 1 ) n(m-1) n(m1)的贡献、
难道这就结束了吗?不,其实还有:
举个例子,abababab
我们发现出现子串交替的时候,就会出现一些重复。
在程序实现的时候,其实可以维护末尾为 i − 2 i-2 i2 i i i的最长公共子串的长度,这个长度加一就是 i i i这个位置要减去的贡献。

似乎也就这两种情况了,我不会证明还有没有别的情况。
这种东西在比赛时是极难推出来的,我们姑且将它们算作找规律罢……


代码

using namespace std;
#include <cstdio>
int n,m;
char str[100010];
int main(){
	scanf("%d%d%s",&n,&m,str+1);
	long long ans=n*(m-1),len=0;
	for (int i=2;i<=n;++i){
		len=(str[i-2]==str[i]?len+1:0);
		if (str[i-1]!=str[i])
			ans+=n*(m-1)-len-1;
	}
	printf("%lld\n",ans);
	return 0;
}

总结

比赛时一定要学会找规律……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值