甲苯先生的字符串·题解
首先,我们可以很容易地想到30分的暴力dp。
\[ dp[i][j] = \Sigma w[k][j]\times dp[i - 1][k] \]
对于\(100\%\)的数据,我们考虑将\(dp[x]\)数组表示为一个\(26\)列的行矩阵,\({dp[x]}_i\)表示到达第\(x\)位后第\(x\)位以\(i + 'a'\)结尾的方案数。将\(w\)数组表示为一个\(26\times 26\)的矩阵\(X\),\(w[i][j] \Leftarrow \Rightarrow\)字符\(i + 'a'\)后可以跟随字符\(j + 'a'\)。
由矩阵乘法的规则(\(\Sigma\)符号表示对矩阵内所有元素求和):
\[ dp[1] = [1\quad1\quad\cdots\quad1] \]
\[ dp[i] = dp[i - 1]\times X \]
\[ ans = \Sigma dp[n] = \Sigma(dp[1]\times X^{n - 1}) \]
利用快速幂求得\(X^{n - 1}\),再乘以\(dp[1]\)即为答案矩阵,复杂度为\(\Theta(\log n)\)。
注意:单位矩阵是主对角线(左上到右下)的数为\(1\),其余数为\(0\)的矩阵。
把字符作为顶点,连接由当前字符指向所有可放在下一位字符的有向边,就构造了更为一般化的图论模型。参考这道题。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MOD(1000000007);
long long n;
char s[100001];
struct Matrix {
int n[26][26];
Matrix operator*(const Matrix &b) const{
register long long t;
Matrix y;
memset(&y, 0, sizeof(y));
for (int i = 0; i < 26; ++i)
for (int j = 0; j < 26; ++j)
for (int k = 0; k < 26; ++k) {
t = (long long)n[i][k] * b.n[k][j];
t >= MOD && (t %= MOD);
y.n[i][j] += t;
y.n[i][j] >= MOD && (y.n[i][j] -= MOD);
}
return y;
}
}ss, r;
int main()
{
int l, ans(0);
long long t;
scanf("%lld", &n);
scanf("%s", s);
l = strlen(s);
for (int i = 0; i < 26; ++i)
for (int j = 0; j < 26; ++j)
ss.n[i][j] = 1;
for (int i = 1; i < l; ++i)
ss.n[s[i - 1] - 'a'][s[i] - 'a'] = 0;
for (int i = 0; i < 26; ++i)
r.n[i][i] = 1;
t = n - 1;
while (t) {
if (t & 1)
r = r * ss;
ss = ss * ss;
t >>= 1;
}
for (int i = 0; i < 26; ++i)
for (int j = 0; j < 26; ++j) {
ans += r.n[i][j];
ans >= MOD && (ans -= MOD);
}
printf("%d\n", ans);
return 0;
}