我这么菜怎么可能会标算的神仙解法?
我们发现如果直接考虑有一个子串<S的话,是很有难度的。
不妨转换为没有子串<S,也就是把T丢到S的KMP自动机 上,一直跑,注意只能走合法边。
合法的意思是假设现在匹配了S[1…x],新加一个字符c,不存在 s [ 1.. y ] = s [ x − y + 1.. x ] , 且 s [ y + 1 ] > c ( y 可 以 = 0 ) s[1..y]=s[x-y+1..x],且s[y+1]>c(y可以=0) s[1..y]=s[x−y+1..x],且s[y+1]>c(y可以=0)
那这个东西怎么计数呢?
如果已经有无限个T拼起来,再加一个T,在自动机上的点不会变。
也就是从自动机上一个点出发,走完T之后,回到原点的方案数。
那么只需要枚举起点,就可以得到一个 O ( n 2 m ) O(n^2m) O(n2m)的做法。
再想想有什么美妙的性质,一个点的合法出边中,只有至多一条不是转移到0的,这个结论可以由合法边的判定得到。
那么枚举这个点走了多少步走到0,后面的事情只用对0做一个预处理的dp。
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i < B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
const int N = 2005;
int m, n;
char s[N];
int nt[N], to[N][26];
const int mo = 998244353;
ll f[N][N], ans;
int main() {
freopen("repeat.in", "r", stdin);
freopen("repeat.out", "w", stdout);
scanf("%d", &m);
scanf("%s", s + 1); n = strlen(s + 1);
fo(i, 1, n) s[i] -= 'a';
int x = 0;
fo(i, 2, n) {
while(x && s[x + 1] != s[i]) x = nt[x];
x += s[x + 1] == s[i];
nt[i] = x;
}
fo(i, 0, n) {
fo(j, 0, 25) {
to[i][j] = -1; int ky = 1;
int x = i == n ? nt[i] : i;
while(x) ky &= s[x + 1] <= j, x = nt[x];
ky &= s[x + 1] <= j;
x = i == n ? nt[i] : i;
while(x && s[x + 1] != j) x = nt[x];
x += s[x + 1] == j;
if(ky) to[i][j] = x;
}
}
f[0][0] = 1;
fo(i, 0, m - 1) {
fo(j, 0, n) if(f[i][j]) {
fo(c, 0, 25) if(to[j][c] != -1) {
f[i + 1][to[j][c]] += f[i][j];
}
}
fo(j, 0, n) f[i + 1][j] %= mo;
}
ans = 1; fo(i, 1, m) ans = ans * 26 % mo;
fo(i, 0, n) {
int x = i;
fo(t, 0, m - 1) {
fo(c, 0, 25) if(to[x][c] == 0)
ans -= f[m - (t + 1)][i];
int y = -1;
fo(c, 0, 25) if(to[x][c] > 0)
y = to[x][c];
x = y;
if(y == -1) break;
}
if(x == i) ans --;
}
ans = (ans % mo + mo) % mo;
pp("%lld\n", ans);
}