题目大意:
现在有一个长度为n的串S,其中每一个字母都是前m个小写字母
计算有多少个不同的长度为n的T(其中T也是由前m个小写字母组成),并且S与T的LCS为n-1
LCS就是同时存在于S和T的最长子序列
分析:
对于一个
而言,其实可以相等于从
中取出一个字母,然后再扔回去一个字母
假如
,连续相同的一段字母内我们任取一个再任扔回去的方案数是
,那么其他的也是
,可是因为他们构成的
是相同的,所以连续相同的一段字母内,我们只需要计算一个
,
怎么计算
呢?
对于
而言我们删掉一个
,那么
,此时我们可以有
个位置可以放置,每个位置有
种字符,那么就有
种方案数,
可是他在自己的块内是不能丢已经删掉的
的,总共有
个位置不能放
,
在这一块前面的其他块长度之和为
,有
个位置可以放置,其实假如第
位字母是
,我在前面放
,和在后面放
,其效益都是
,所以也算重了,总共就有
种,
后面的块同理也有
种,所以算重的为前面块长度和+后面块长度和,不合法的为当前块原先的长度和,即当前块长度和+1
总共也就是
,所以
然而还有一个情况会算重,
例如像
这样的连续段,相邻的
,a后移跟b前移是等价的,
中首个
后移到末尾,和末尾b前移到首位是相同的,那么如果有
长的串是这样的,那么他有
个这样的子串,就减去它
代码:
#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;
}