[TJOI2019] 甲苯先生的字符串
算法
一眼计数 \(\rm{dp}\) , 不会, 下播
好的让我们分析一下题意
问题可以简单地转化为对于每一个字母, 只能选择另外的某些字母作为下一个, 求一共的可能性
简单的, 我们找出 \(\rm{dp}\) 方程, 令 \(f_{i, s}\) 表示考虑前 \(i\) 个字母, 以 \(s\) 结尾的可能性数量, 转移不用多说
\[f_{i, s} = \sum f_{i - 1, s^{\prime}}
\]
所以怎么快速转移这个柿子
矩阵艹一下, 过了
实现
矩阵入门题, 打一下过个板子
框架
首先预处理每一种字符可以由什么字符转移而来, 然后建矩阵
代码
#include <bits/stdc++.h>
#define int long long
const int MAXN = 30;
const int MOD = 1e9 + 7;
int n;
struct Matrix {
int mar[MAXN][MAXN];
bool type; // false 表示 n * n , true 表示 n * 1
} ;
Matrix operator * (const Matrix& a, const Matrix& b) {
Matrix ans; memset(ans.mar, 0, sizeof(ans.mar));
int tmp; tmp = (a.type || b.type) ? 1 : n;
for (int i = 1; i <= 26; i++)
for (int j = 1; j <= 26; j++)
for (int k = 1; k <= 26; k++) {
ans.mar[i][j] += (a.mar[i][k] % MOD) * (b.mar[k][j] % MOD);
ans.mar[i][j] %= MOD;
}
return ans;
}
Matrix fastpow(Matrix a, int pow) {
Matrix ans;
memset(ans.mar, 0, sizeof(ans.mar)); for (int i = 1; i <= 26; i++) ans.mar[i][i] = 1;
while (pow) {
if (pow & 1) ans = ans * a; a = a * a;
pow >>= 1;
}
return ans;
}
std::string s1;
bool CanTurn[30][30];
Matrix ini, powmar;
void init() {
memset(CanTurn, true, sizeof(CanTurn));
for (int i = 1; i < s1.size(); i++)
CanTurn[s1[i] - 'a' + 1][s1[i - 1] - 'a' + 1] = false;
for (int i = 1; i <= 26; i++) ini.mar[i][1] = 1; ini.type = true;
for (int i = 1; i <= 26; i++)
for (int j = 1; j <= 26; j++)
if (CanTurn[i][j]) powmar.mar[i][j] = 1; else powmar.mar[i][j] = 0;
}
signed main()
{
scanf("%lld", &n);
std::cin >> s1;
init();
powmar = fastpow(powmar, n - 1);
powmar = powmar * ini;
int Ans = 0;
for (int i = 1; i <= 26; i++) {
Ans += powmar.mar[i][1];
Ans %= MOD;
}
printf("%lld", Ans);
return 0;
}
总结
不好评价的简单题, 只是说我不会打矩乘
记得取模