题目要求的是不包含给定字符串的所有字符串的数量,可以转化为求包含长度在 0 到 n-1 的字符串的数量。
使用动态规划,设状态 f(i, j) 表示长度为 i 的字符串且与给定字符串的前缀最大匹配长度为 j 的字符串数量。在状态转移时,枚举给定字符串的长度 j,如果长度为 i+1 的字符串可以匹配长度为 j 的给定字符串前缀,那么就可以从 f(i, j) 转移到 f(i+1, k),其中 k 是给定字符串前缀的最大公共前缀的长度。具体的转移方式可以看上面的公式。
为了方便转移,可以使用一个二维数组 a 来记录不同长度的给定字符串前缀的最大公共前缀的长度。如果 f(i, j) 可以转移到 f(i+1, k),则让 a[j][k]++,即让 f(i+1, k) += f(i, j)。
最终答案就是所有状态 f(i, j) 的值之和。
代码:
import java.util.*;
public class Main {
static int[][] a = new int[25][25];
static int[] ne = new int[25];
static char[] str = new char[25];
public static void mul(int[][] c, int[][] a, int[][] b, int mod) {
int n = a.length;
int m = b[0].length;
int k = a[0].length;
int[][] f = new int[n][m];
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
for (int l = 0; l < k; ++l) {
f[i][j] = (f[i][j] + a[i][l] * b[l][j]) % mod;
}
}
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
c[i][j] = f[i][j];
}
}
}
public static int qmi(int k, int mod) {
int n = a.length;
int[][] f0 = new int[n][n];
f0[0][0] = 1;
while (k > 0) {
if ((k & 1) == 1) {
mul(f0, f0, a, mod);
}
mul(a, a, a, mod);
k >>= 1;
}
int res = 0;
for (int i = 0; i < n; ++i) {
res = (res + f0[0][i]) % mod;
}
return res;
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
int mod = scan.nextInt();
String s = scan.next();
str = s.toCharArray();
for (int i = 2, j = 0; i <= m; ++i) {
while (j > 0 && str[i - 1] != str[j]) {
j = ne[j];
}
if (str[i - 1] == str[j]) {
++j;
}
ne[i] = j;
}
for (int j = 0; j < m; ++j) {
for (char c = '0'; c <= '9'; ++c) {
int k = j;
while (k > 0 && str[k] != c) {
k = ne[k];
}
if (str[k] == c) {
++k;
}
if (k < m) {
++a[j][k];
}
}
}
System.out.println(qmi(n, mod));
}
}