我们称一个字符串是优美的,当且仅当这个字符串中不存在长度严格大于 2 的回文串。
现在有 m 种不同的字符,那么在可以组成的长度恰好为 n 的 m的n次方 个不同的字符串中,请求出一共有多少个字符串是优美的,输出答案对 1000000007 取模后的结果即可。
Input
输入仅一行两个整数,分别表示 n 和 m(1≤n≤1e6,1≤m≤1e9)。
Output
输出仅一行一个整数,表示答案对 1000000007 取模后的结果。
Sample Input 1
3 3
Sample Output 1
18
代码长度限制
16 KB
时间限制
1000 ms
内存限制
256 MB
分析:
分析字符串特点发现2以内的没有限制,最小出现分类的从长度为3的字符串开始,以长度为3的字符串结尾分类讨论,具有以下几种形式:
1.abc 三个字母都不相同
2.aab 前两个字母相同最后一个不i同
3.abb 后两个字母相同第一个不同
其他情况都不符合题意,因此可以开二维数组f[i][0/1/2]三种来分别表示以上三种情况长度为n的字符串方案数量。
第一种:
以abc形式结尾的长度为i的字符串的方案数,那么就要保证i之前的两个字符必须不相同,最后两位不相同的有abc和aab两种,最后一个字符也就是当前i所在位置的字符与前两个字符也不能一样,所以i处可以放入的字符有(m-2)种,因此递推方程就是:
f[i][0] = f[i-1][0] * (m - 2) + f[i-1][1] *(m-2).
第二种:
以aab结尾的长度为i的方案数,保证前两位相同,最后两位相同的有abb一种,xaab字符串因为要满足不是回文字符串,因此i处的字符也只有m-2种,递推方程:
f[i][1] = f[i-1][2] * (m-2).
第三种:
以abb结尾的长度为i的方案数,和上面一样,最后两位不同的有abc和aab两种,因为i处的字符要与前一个相同,因此i处的字符就只有1种。递推方程就是:
f[i][2] = f[i-1][0] + f[i-1][1].
数据范围过大要在计算过程中边算边取模,防止爆long long 。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
const int mod=1000000007;
ll f[N][4];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
ll m;
cin>>n>>m;
f[3][0]=m*(m-1)%mod*(m-2)%mod;
f[3][1]=m*(m-1)%mod;
f[3][2]=m*(m-1)%mod;
for(int i=4;i<=n;i++)
{
f[i][0]=(f[i-1][0]*(m-2)%mod+f[i-1][1]*(m-2)%mod)%mod;
f[i][1]=f[i-1][2]*(m-2)%mod;
f[i][2]=(f[i-1][0]+f[i-1][1])%mod;
}
ll ans=0;
for(int i=0;i<3;i++) ans+=f[n][i];
if(n==1) cout<<m<<'\n';
else if(n==2) cout<<m*m<<'\n';
else cout<<ans%mod<<'\n';
}