[HNOI2008]GT考试

题意

有一个长度为\(n\)\((n\le1e^9)\)只由阿拉伯数字组成的串\(A\),现在给一个长度为\(m\)\((m\le20)\)同样只由阿拉伯数字组成的串\(B\),求满足条件的\(A\)串个数,条件:\(B\)串不包含在\(A\)串。

题解

\(dp[i][j]=\)长度为\(i\)且末尾(后缀)已经与\(B\)串首部(前缀)匹配了\(j\)位的满足条件的串的方案数。则:$$dp[i][j] = \sum_{k = 0}^{m - 1}dp[i - 1][k]a[k][j]$$ \(a[k][j]=\) 已经匹配了\(k\)位现在再加一个数字而能匹配\(j\)位的方案数。比如\(B\)串为\(12315\)\(a[1][1] = 1\)就是\(1 + ?\)能与\(12315\)匹配\(1\)位的方案数是1的\(?\)的取值个数(\(? = 1\))。这里讲的匹配是指\(1?\)的后缀与\(12315\)的前缀相同的串的长度。$$a =
\begin{pmatrix}
9 & 1 & 0 & 0 & 0 & 0 \
8 & 1 & 1 & 0 & 0 & 0 \
8 & 1 & 0 & 1 & 0 & 0 \
9 & 0 & 0 & 0 & 1 & 0 \
7 & 1 & 1 & 0 & 0 & 1
\end{pmatrix}$$行代表\(k\),列代表\(j\)。如何求出\(a\)数组?既然是在求公共的前缀和后缀,故联想到\(KMP\)\(Next\)数组。具体做法见代码。观察上面的转移方程,系数都是常数且是一次方程,自然而然的转化为矩阵的乘积形式,\(m\) = 5,\(B\)串为:12315为例$$\begin{pmatrix}
dp[i][0] & \dots & dp[i][m - 1]
\end{pmatrix}=
\begin{pmatrix}
dp[i - 1][0] & \dots & dp[i - 1][m - 1]
\end{pmatrix}

\begin{pmatrix}
9 & 1 & 0 & 0 & 0 \
8 & 1 & 1 & 0 & 0 \
8 & 1 & 0 & 1 & 0 \
9 & 0 & 0 & 0 & 1 \
7 & 1 & 1 & 0 & 0
\end{pmatrix}$$因为是求不包含\(B\)串的A串个数,故\(a\)矩阵的最后一列应该忽略。

const int N = 100005;
 
int mod;
 
struct mat {
    int t;
    int A[22][22];
    mat() {
        mem(A, 0);
    }
    void Inite(int m) {
        t = m;
    }
    mat operator * (const mat& tp) {
        mat ans;
        ans.t = tp.t;
        rep(i, 0, t) rep(j, 0, t) rep(k, 0, t) {
            ans.A[i][j] += A[i][k] * tp.A[k][j];
            ans.A[i][j] %= mod;
        }
        return ans;
    }
    void operator = (const mat& tp) {
        t = tp.t;
        rep(i, 0, t) rep(j, 0, t) A[i][j] = tp.A[i][j];
    }
};
 
int n, m;
int nxt[22];
 
string s;
 
mat qpow(mat C, int x) {
    mat B;
    B.Inite(m);
    rep(i, 0, m) B.A[i][i] = 1;
    for (; x; C = C * C, x >>= 1) if (x & 1) B = B * C;
    return B;
}
 
void Next(){
    nxt[0] = nxt[1] = 0;
    rep(i, 1, m) {
        int k = nxt[i];
        while(k && s[k] != s[i]) k = nxt[k];
        nxt[i + 1] = (s[k] == s[i] ? k + 1 : 0);
    }
}
 
int main()
{
    cin >> n >> m >> mod >> s;
 
    Next();
 
    mat B;
    B.Inite(m);
 
    rep(i, 0, m) for (char j = '0'; j <= '9'; ++j) {
        int k = i;
        while(k && s[k] != j) k = nxt[k];
        if (s[k] == j) k++;
        if (k != m) B.A[i][k]++;
    }
 
    mat C = qpow(B, n);
 
    mat D;
    D.Inite(m);
    D.A[0][0] = 1;
 
    C = D * C;
 
    int ans = 0;
    rep(i, 0, m) ans = (ans + C.A[0][i]) % mod;
    cout << ans << endl;
 
 
    return 0;
}

posted @ 2018-09-30 13:55  天之道,利而不害  阅读(716)  评论(0编辑  收藏  举报