甲苯先生的字符串·题解

甲苯先生的字符串·题解

首先,我们可以很容易地想到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;
}

猜你喜欢

转载自www.cnblogs.com/justlikeoldtimes/p/10847435.html
今日推荐