[HNOI2008]GT考试 - KMP + 矩阵快速幂

Description

求有多少个长度为n的0-9的数字串不包含一个给定的长度为m的数字串。

Solution

在kmp自动机上dp

40pts kmp + dp

考虑\(dp\),设\(f_{i,j}\)表示前\(i\)个字符,匹配到给定串的第\(j\)位的方案数

转移时枚举下一位放什么字符,然后用\(kmp\)处理出会匹配到哪一位,然后从\(f_{i,j}\)转移到\(f_{i,nxt}\)。需要注意的是,如果匹配到的下一位为\(m\),那么不需要进行转移。

最终答案为\(\sum \limits_{i=0}^{m-1} f[n][i]\)

Code

#include <bits/stdc++.h>
using namespace std;

const int _ = 20 + 10;
const int __ = 1e6 + 10;
int N, M, mod, nxt[_];
char s[_];
int f[__][_];

inline void kmp() {
  nxt[1] = 0;
  for (int i = 2, j = 0; i <= M; ++i) {
    while (j > 0 && s[i] != s[j + 1]) j = nxt[j];
    if (s[i] == s[j + 1]) ++j;
    nxt[i] = j;
  }
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("GT.in", "r", stdin);
  freopen("GT.out", "w", stdout);
#endif
  scanf("%d%d%d", &N, &M, &mod);
  scanf("%s", s + 1);
  kmp();

  f[0][0] = 1;
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j <= M; ++j) {
      for (int k = 0; k <= 9; ++k) {
        int tmp = j;
        while (tmp > 0 && k != s[tmp + 1] - '0') tmp = nxt[tmp];
        if (k == s[tmp + 1] - '0') ++tmp;
        if (tmp < M) f[i + 1][tmp] = (f[i + 1][tmp] + f[i][j]) % mod;
      }
    }
  }

  int ans = 0;
  for (int i = 0; i < M; ++i) ans = (ans + f[N][i]) % mod;
  printf("%d\n", ans);
  return 0;
}

100pts 矩阵加速转移

实际上,并不需要每次枚举字母,我们可以预处理出从一个匹配长度加一个字符变成另一个匹配长度的方案数,设为\(g_{i,j}\)

\[f_{i,j}=\sum \limits_{k=0}^{m-1} f_{i-1,k} \times g_{k,j} \]

这个式子显然是可以矩阵优化的,于是矩阵快速幂之后,就可以通过本题了。

Code

#include <bits/stdc++.h>
using namespace std;

const int _ = 23;
int N, M, mod, nxt[_], cnt[_][_];
char s[_];

struct Matrix {
  int A[_][_];
  Matrix() { memset(A, 0, sizeof(A)); }
  Matrix operator*(const Matrix &b) const {
    Matrix c;
    memset(c.A, 0, sizeof(c.A));
    for (int i = 0; i < M; ++i)
      for (int j = 0; j < M; ++j)
        for (int k = 0; k < M; ++k)
          c.A[i][j] = (c.A[i][j] + A[i][k] * b.A[k][j] % mod) % mod;
    return c;
  }
} F, G;

inline void kmp() {
  nxt[1] = 0;
  for (int i = 2, j = 0; i <= M; ++i) {
    while (j > 0 && s[i] != s[j + 1]) j = nxt[j];
    if (s[i] == s[j + 1]) ++j;
    nxt[i] = j;
  }
  for (int i = 0; i < M; ++i) {
    for (int j = '0'; j <= '9'; ++j) {
      int k = i;
      while (k > 0 && j != s[k + 1]) k = nxt[k];
      if (j == s[k + 1]) ++k;
      if (k < M) ++cnt[i][k];
    }
  }
}

Matrix ksm(Matrix a, int b) {
  Matrix ret;
  for (int i = 0; i < M; ++i) ret.A[i][i] = 1;
  for (; b; b >>= 1) {
    if (b & 1) ret = ret * a;
    a = a * a;
  }
  return ret;
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("GT.in", "r", stdin);
  freopen("GT.out", "w", stdout);
#endif
  scanf("%d%d%d", &N, &M, &mod);
  scanf("%s", s + 1);
  kmp();

  for (int i = 0; i < M; ++i)
    for (int j = 0; j < M; ++j) G.A[i][j] = cnt[i][j];
  F.A[0][0] = 1;
  G = ksm(G, N);
  F = F * G;
  int ans = 0;
  for (int i = 0; i < M; ++i) ans = (ans + F.A[0][i]) % mod;
  printf("%d\n", ans);
  return 0;
}
posted @ 2020-01-03 15:42  newbielyx  阅读(111)  评论(0编辑  收藏  举报