zoukankan      html  css  js  c++  java
  • [JZOJ4673] 【NOIP2016提高A组模拟7.20】LCS again

    题目

    描述

    在这里插入图片描述

    题目大意

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


    思考历程

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


    正解

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

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


    代码

    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
    ",ans);
    	return 0;
    }
    

    总结

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

  • 相关阅读:
    HDU 4024 Dwarven Sniper’s hunting(数学公式 或者是二分)
    二分图最大匹配总结
    HDU 4022 Bombing (STL应用)
    HDU 1847 Good Luck in CET4 Everybody!(组合博弈)
    HDU 1556 Color the ball(树状数组)
    HDU 4023 Game(博弈)
    HDU 1406 完数(水题)
    HDU 4021 24 Puzzle
    Oracle 多表查询优化
    【编程之美】字符串移位包含的问题(续)
  • 原文地址:https://www.cnblogs.com/jz-597/p/11145232.html
Copyright © 2011-2022 走看看