P3041 Video Game G
P3041 Video Game G
题意:
一个字符串由,ABC三个字符构成,给出\(n\)个由A,B,C组成的组合技,求怎么安排字符串可以使得组合技最多。
思路:
因为如果当前的后缀可以的话,现在也必然可以,所以在构建AC自动机的时候,要加上cnt[ne[t]]
也就是可以的后缀数量,因为是一层一层的处理,所以之前的后缀已经累加到了当前的后缀上。
实现:
#include <stdio.h>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1005;
char s[N];
int tr[N][3], idx = 1, cnt[N], nex[N], q[N], f[N][N];
void insert() //字典树中插入字符串
{
int p = 0;
for (int i = 1; s[i]; i++)
{
int t = s[i] - 'A';
if (!tr[p][t])
tr[p][t] = idx++;
p = tr[p][t];
}
cnt[p]++;
}
//构造nex数组
void build()
{
int hh = 1, tt = 0;
for (int i = 0; i < 3; i++)
if (tr[0][i])
q[++tt] = tr[0][i];
while (hh <= tt)
{
int t = q[hh++]; // i - 1
for (int i = 0; i < 3; i++)
{
//注意下面要改变tr[t][i]值,所以要加引号,p相当于,当前需要匹配的位置
int &p = tr[t][i];
if (!p)
// nex[t]是之前匹配到的位置
p = tr[nex[t]][i];
else
{
nex[p] = tr[nex[t]][i];
q[++tt] = p;
cnt[p] += cnt[nex[p]];
}
}
}
}
int main()
{
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
{
scanf("%s", s + 1);
insert();
}
build();
memset(f, 0xcf, sizeof(f));
f[0][0] = 0;
int res = 0;
//前i个,匹配到j
for (int i = 1; i <= k; i++)
for (int j = 0; j < idx; j++)
for (int h = 0; h < 3; h++)
{
int p = tr[j][h];
f[i][p] = max(f[i][p], f[i - 1][j] + cnt[p]);
if (i == k)
res = max(res, f[i][p]);
}
printf("%d", res);
return 0;
}