Jzoj P4673 LCS again___容斥原理

版权声明:欢迎借鉴,谢绝抄搬。 https://blog.csdn.net/Gx_Man_VIP/article/details/88783311

题目大意:

现在有一个长度为n的串S,其中每一个字母都是前m个小写字母
计算有多少个不同的长度为n的T(其中T也是由前m个小写字母组成),并且S与T的LCS为n-1
LCS就是同时存在于S和T的最长子序列

n < = 100000 n<=100000

分析:

对于一个 T T 而言,其实可以相等于从 S S 中取出一个字母,然后再扔回去一个字母
假如 S = a a a b b b c c c S=aaabbbccc ,连续相同的一段字母内我们任取一个再任扔回去的方案数是 x x ,那么其他的也是 x x ,可是因为他们构成的 T T 是相同的,所以连续相同的一段字母内,我们只需要计算一个 x x
怎么计算 x x 呢? x = n m n x=n*m-n
对于 S = a a a b b b c c c S=aaa|bbb|ccc 而言我们删掉一个 b b ,那么 S = a a a b b c c c S=aaa|bb|ccc ,此时我们可以有 n n 个位置可以放置,每个位置有 m m 种字符,那么就有 n m n*m 种方案数,
可是他在自己的块内是不能丢已经删掉的 b b 的,总共有 3 3 个位置不能放 b b
在这一块前面的其他块长度之和为 3 3 ,有 4 4 个位置可以放置,其实假如第 i i 位字母是 A A ,我在前面放 A A ,和在后面放 A A ,其效益都是 A A AA ,所以也算重了,总共就有 4 1 4-1 种,
后面的块同理也有 4 1 4-1 种,所以算重的为前面块长度和+后面块长度和,不合法的为当前块原先的长度和,即当前块长度和+1
总共也就是 n n ,所以 x = n m n x=n*m-n
然而还有一个情况会算重,
例如像 a b a b a b ababab 这样的连续段,相邻的 a b ab ,a后移跟b前移是等价的, a b a b abab 中首个 b b 后移到末尾,和末尾b前移到首位是相同的,那么如果有 l e n len 长的串是这样的,那么他有 l e n ( l e n 1 ) / 2 len*(len-1)/2 个这样的子串,就减去它

代码:

#include <iostream>
#include <cstdio>
#include <queue>
#include <cmath>
#include <cstring>
#include <algorithm>

#define N 100005

using namespace std;

const int mo = 100007;

typedef long long ll;
 
ll ans, now;
char s[N];
int n, m;

int main()
{
    scanf("%d %d", &n, &m);
	scanf("%s", s + 1);
    ans = n * (m - 1);
    for (int i = 2; i <= n; i++) 
	    if (s[i] != s[i - 1]) ans += n * (m - 1); 
	int now = 1;
    for (int i = 2; i <= n; i++)
	{
        if (now == 1)
		{
		   if (s[i] != s[i - 1]) now++; 
		}
        else
		{
            if (s[i] == s[i - 2]) now++;
            else
			{
                ans -= (now - 1) * now / 2; now = 1;
                if (s[i] != s [i - 1]) now = 2;
            }     
        }
    }
    ans -= (now - 1) * now / 2;
    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Gx_Man_VIP/article/details/88783311
LCS
今日推荐