Description
Input
Output
Sample Input
【样例输入1】
3 3
aaa
【样例输入2】
3 3
aab
【样例输入3】
1 2
a
【样例输入4】
10 9
abacadefgh
Sample Output
【样例输出1】
6
【样例输出2】
11
【样例输出3】
1
【样例输出4】
789
Data Constraint
Solution
考虑用算重的方案数-重复的方案数。
我们可以把原串以相同字符为一块分成很多块,因为同一块内字符都有同样的作用。
像 aaabbacc 这样的字符串,就变成 aaa|bb|a|cc ,每一块内都统计一次答案。
一个字符被挖去后,有 n 个位置可以选,每个位置又有
m 种字符可以填。但是每个位置又有一种字符会重复,如 abc ,把 c 挖去,则无论填 a 的左边还是右边都一样。
这样 n 个位置都重复一遍 ,于是每个块都贡献
n∗m−n 的答案。但是这样还是会算重!
比如说字符串 abab ,把第三个字符 a 挖去后是 abb ,接到后边是 abba 。
而把第四个字符 b 挖去后是 aba ,接到倒数第二个位置是 abba 。
它们是一样的!我们发现如果是隔位相同的也会算重一次,
而且隔的位数 k 每多一,它就与前面每个字符都重复一遍——它满足等差数列的形式。
即第一个字符重复 0 次,第二个字符重复 1 次,第三个字符重复 2 次……
那么这个长度为
k 的隔位相同的串就重复了 k∗(k−1)2 次,减去即可。时间复杂度 O(N) 。
Code
#include<cstdio>
using namespace std;
typedef long long LL;
int n,m,k=1;
LL ans=1;
char s[100005];
int main()
{
scanf("%d%d",&n,&m);
scanf("%s",s+1);
for(int i=2;i<=n;i++) ans+=s[i]!=s[i-1];
ans*=(LL)n*m-n;
for(int i=2;i<=n;i++)
if(k==1) k+=s[i]!=s[i-1]; else
if(s[i]==s[i-2]) k++; else
{
ans-=(LL)k*(k-1)>>1;
k=(s[i]!=s[i-1])+1;
}
ans-=(LL)k*(k-1)>>1;
printf("%lld",ans);
return 0;
}