题解:CF696D Legen...
更好的阅读体验
前置知识:AC 自动机。
Solution
看到子串出现次数,首先会想到建 AC 自动机,这样我们就处理掉了子串出现次数的问题。
但是它带了一个权值
那么我们肯定是从根往子节点一路跳,就有:
但是你看到
诶!当我们转移的时候有且仅有
为符合转移的运算我们把矩乘重定义为加法然后取
Code
#include <bits/stdc++.h>
#define int long long
#define fail(now) Amt[now].fail
#define end(now) Amt[now].end
#define son(now,x) Amt[now].son[x]
using namespace std;
const int MAXN = 210;
const int MAXS = 200010;
int N, M, cnt, ans;
int val[MAXN];
string Style;
struct Amton {
int son[26];
int fail, end;
} Amt[MAXS];
struct Matt {
int mat[MAXN][MAXN];
Matt() { memset (mat, -0x3f, sizeof(mat)); }
Matt operator * (const Matt &s) const {
Matt res = Matt();
for (int k = 0; k <= cnt; k ++) {
for (int i = 0; i <= cnt; i ++) {
for (int j = 0; j <= cnt; j ++)
res.mat[i][j] = max (res.mat[i][j], mat[i][k] + s.mat[k][j]);
}
}
return res;
}
} base, final;
Matt binpow (Matt x, int p) {
Matt res = x;
for (--p; p; p >>= 1) {
if (p & 1)
res = res * x;
x = x * x;
}
return res;
}
inline void InsertString (int val) {
int now = 0, len = Style.length();
for (int i = 0; i < len; i ++) {
int chVal = Style[i] - 'a';
if (!son(now, chVal))
son(now, chVal) = ++cnt;
now = son(now, chVal);
}
end(now) += val;
}
inline void GetFailPointer() {
queue<int> q;
for (int i = 0; i < 26; i ++) {
if (son(0, i))
q.emplace (son(0, i));
}
while (!q.empty()) {
int now = q.front(); q.pop();
end(now) += end(fail(now));
for (int i = 0; i < 26; i ++) {
if (!son(now, i))
son(now, i) = son(fail(now), i);
else {
fail(son(now, i)) = son(fail(now), i);
q.emplace (son(now, i));
}
}
}
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> N >> M, base = Matt();
for (int i = 1; i <= N; i ++) cin >> val[i];
for (int i = 1; i <= N; i ++) cin >> Style, InsertString(val[i]);
GetFailPointer();
for (int i = 0; i <= cnt; i ++) {
for (int j = 0; j < 26; j ++)
base.mat[i][son(i, j)] = end(son(i, j));
}
final = binpow (base, M);
ans = -0x7f7f7f7f7f7f;
for (int i = 0; i <= cnt; i ++)
ans = max (ans, final.mat[0][i]);
cout << ans << '\n';
return 0;
}