題目
time limit per test: 6 seconds
memory limit per test: 256 megabytes
input: standard input
output: standard output
Description
Barney was hanging out with Nora for a while and now he thinks he may have feelings for her. Barney wants to send her a cheesy text message and wants to make her as happy as possible.
Initially, happiness level of Nora is . Nora loves some pickup lines like “I’m falling for you” and stuff. Totally, she knows pickup lines, each consisting only of lowercase English letters, also some of them may be equal (in writing, but different in pronouncing or meaning though). Every time Nora sees -th pickup line as a consecutive subsequence of Barney’s text message her happiness level increases by . These substrings may overlap, for example, Nora will see the pickup line aa
twice and the pickup line ab
once in text message aaab
.
Due to texting app limits, Barney’s text may have up to l characters.
Barney asked you to help him make Nora as much happy as possible, it’s gonna be legen…
Input
The first line of input contains two integers and (, ) — the number of pickup lines and the maximum length of Barney’s text.
The second line contains integers (), meaning that Nora’s happiness level increases by after every time seeing -th pickup line.
The next lines contain the pickup lines. -th of them contains a single string consisting of only English lowercase letter. Summary length of all pickup lines does not exceed .
All strings are not empty.
Output
Print the only integer — the maximum possible value of Nora’s happiness level after reading Barney’s text.
Examples
input
3 6
3 2 1
heart
earth
art
output
6
input
3 6
3 2 8
heart
earth
art
output
16
Note
An optimal answer for the first sample case is hearth containing each pickup line exactly once.
An optimal answer for the second sample case is artart.
分析
不管怎麼說,這是我第一道矩陣加速的題(第二道AC自動機),寫了2個小時,在CSP前兩天的時候調了一下午。
一道題,會越做越覺得水,當我畫了4+小時把它gan完,覺得它已經變成汪洋大海了。
構造字符串的套路,就是建踹圖。大家可以先看一下這道題(的題解):POJ2778 DNA Sequence。
踹(Trie)圖是什麼,就是你的AC自動機搞完fail
指針並且傳遞好信息過後的圖(fail
指針不是邊)。這個圖有幾個美妙的性質:
- 踹圖強聯通;
- 你從根開始隨便走,可以得到一個字符串,並且想要多長有多長;
- 由於模式串的信息是存在尾字符對應的結點上的且你搞
fail
的時候進行了傳遞,那麼中所有結點的信息就是所有模式子串的信息。
現在我們就可以在本題的踹圖上DP:表示由個字符組成的字符串,得到的最大快樂值,那麼注意踹圖不是樹。
是這個字符串的所有後綴的快樂值之和,可以在GetFail
裏面再DP一下(把自己的快樂值加上自己的fail
點的快樂值即可,這也是一個按Bfs
順序的DP)。
於是,在踹圖上做一個(?)的DP,反正把太湖之光借給你都跑不過。
於是,我現在才發現了一個神奇的結論:一個個結點組成的圖的鄰接矩陣的次冪中,表示通過條邊到的方案數。
證明:
,根據定義:相當於枚舉倒數第二個點,進行一個Floyd式的DP,得證。
把它類比到這個題裏面,做出踹圖的鄰接矩陣(有邊權,是邊權快樂值),由於這個是取,所以,上面的式子要變成:(還得滿足:步以內能到達,一步以內能到達)
這玩意有結合律(感性理解海星),所以可以矩陣快速冪。
講完了,還有點細節,-1
表示不連通而不能用0
表示,因爲有的聯通但快樂值爲0
。還有GetFail
的寫法什麼的。還有中途糾結了幾千萬遍根是號店還是號點,最後選擇了像號店屈服。
我不會告訴你我的海口複習法有多好用。
代碼
#include <cstdio>
#include <cstring>
#include <queue>
typedef long long LL;
struct Matrix {
static const int MAXN = 200;
int N, M;
LL A[MAXN +5][MAXN + 5];
Matrix() {
N = M = 0;
memset(A, 0, sizeof A);
}
Matrix(int row, int col, int sign = 0): N(row), M(col) {
memset(A, 0, sizeof A);
for (int i = 1; i <= row; i++)
for (int j = 1; j <= col; j++)
A[i][j] = sign;
}
LL* operator [] (const int &i) {
return A[i];
}
Matrix operator * (Matrix &B) {
Matrix ret(N, B.M, -1);
for (int i = 1; i <= N; i++)
for (int j = 1; j <= M; j++)
for (int k = 1; k <= B.N; k++)
if (A[i][k] != -1 && B[k][j] != -1)
ret[i][j] = std::max(ret[i][j], A[i][k] + B[k][j]);
return ret;
}
void Output() {
puts("");
puts("-- Debug --");
printf("\t");
for (int i = 1; i <= N; i++)
printf("(%d)\t", i);
for (int i = 1; i <= N; i++){
printf("\n(%d)\t", i);
for (int j = 1; j <= M; j++)
printf("%lld\t", A[i][j]);
}
puts("");
}
Matrix operator ^ (LL y) {
Matrix ret(N, N, -1), base = *this;
ret[1][1] = 0;
while (y) {
if (y & 1)
ret = ret * base;
// printf("< ret >"), ret.Output();
y >>= 1;
base = base * base;
// printf("< base >"), base.Output();
}
return ret;
}
};
struct AC_Automaton {
static const int MAXC = 26;
static const int MAXL = 200;
struct Node {
char dbg;
int num;
int ch[MAXC +5], fal;
}T[MAXL + 5];
int NodeCnt;
AC_Automaton() {
NodeCnt = 1, T[1].dbg = '$';
memset(T, 0, sizeof T);
}
int NewNode(const char &cur) {
T[++NodeCnt].dbg = cur;
return NodeCnt;
}
void Insert(const char *str, const int &hps) {
int len = strlen(str), u = 1;
for (int i = 0; i < len; i++) {
int id = str[i] - 'a';
if (!T[u].ch[id])
T[u].ch[id] = NewNode(str[i]);
u = T[u].ch[id];
}
T[u].num += hps;
}
void GetFail() {
std::queue<int> Q;
for (int i = 1; i <= NodeCnt; i++)
T[i].fal = 1;
for (int i = 0; i < MAXC; i++)
if (T[1].ch[i])
Q.push(T[1].ch[i]);
else
T[1].ch[i] = 1;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
T[u].num += T[T[u].fal].num;
for (int i = 0; i < MAXC; i++) {
int v = T[u].ch[i], nxt = T[T[u].fal].ch[i];
if (v) {
Q.push(v);
T[v].fal = nxt;
}
else
T[u].ch[i] = nxt;
}
}
}
};
const int MAXN = 200;
int N; LL L;
int A[MAXN + 5];
char tmp[MAXN + 5];
int main() {
scanf("%d%lld", &N, &L);
for (int i = 1; i <= N; i++)
scanf("%d", &A[i]);
AC_Automaton Trie;
for (int i = 1; i <= N; i++) {
scanf("%s", tmp);
Trie.Insert(tmp, A[i]);
}
Trie.GetFail();
// for (int i = 1; i <= Trie.NodeCnt; i++)
// printf("%c %d Fail = %d\n", Trie.T[i].dbg, Trie.T[i].num, Trie.T[i].fal);
// puts("");
Matrix dp(Trie.NodeCnt, Trie.NodeCnt, -1);
for (int i = 1; i <= Trie.NodeCnt; i++)
for (int j = 0; j < AC_Automaton::MAXC; j++) {
int v = Trie.T[i].ch[j];
dp[i][v] = Trie.T[v].num;
}
// dp.Output();
dp = dp ^ L;
// dp.Output();
LL Ans = 0;
for (int j = 1; j <= dp.M; j++)
Ans = std::max(Ans, dp[1][j]);
printf("%lld", Ans);
}