【Codeforces Round 362 (Div 2)F】【AC自動機+矩陣快速冪】Legen... 長度為l字串最大能夠重複匹配的字串權值

snowy_smile發表於2016-07-28

F. Legen...
time limit per test
6 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

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 0. Nora loves some pickup lines like "I'm falling for you" and stuff. Totally, she knows n 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 i-th pickup line as a consecutive subsequence of Barney's text message her happiness level increases by ai. These substrings may overlap, for example, Nora will see the pickup line aa twice and the pickup line ab once in text messageaaab.

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 n and l (1 ≤ n ≤ 200, 1 ≤ l ≤ 1014) — the number of pickup lines and the maximum length of Barney's text.

The second line contains n integers a1, a2, ..., an (1 ≤ ai ≤ 100), meaning that Nora's happiness level increases by ai after every time seeing i-th pickup line.

The next n lines contain the pickup lines. i-th of them contains a single string si consisting of only English lowercase letter. Summary length of all pickup lines does not exceed 200.

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.


#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 205, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
const int G = 202;//矩陣大小
struct MX
{
	LL v[G][G];
	void O() { MS(v, 0); }
	void F() { MS(v, -63); }
	void E() { MS(v, -63); for (int i = 0; i < G; ++i)v[i][i] = 0; }
	MX operator * (const MX &b) const
	{
		MX c; c.O();
		for (int i = 0; i<G; i++)
		{
			for (int j = 0; j<G; j++)
			{
				c.v[i][j] = -1e18;
				for (int k = 0; k<G; k++)
				{
					gmax(c.v[i][j], v[i][k] + b.v[k][j]);
				}
			}
		}
		return c;
	}
	MX operator ^ (LL p) const
	{
		MX y; y.E();
		MX x; MC(x.v, v);
		while (p)
		{
			if (p & 1)y = y*x;
			x = x*x;
			p >>= 1;
		}
		return y;
	}
}a, b, c;
int id;
char s[N];
int fail[N];
int nxt[N][26];
int cnt[N];
void insert(char s[], int val)
{
	int p = 1;
	for (int i = 0; s[i]; i++)
	{
		int x = s[i] - 'a';
		if (nxt[p][x] == 0)nxt[p][x] = ++id;
		p = nxt[p][x];
	}
	cnt[p]+=val;
}
int getfail(int p, int x)
{
	while (p)
	{
		if (nxt[p][x])return nxt[p][x];
		p = fail[p];
	}
	return 1;
}
void AC_automation()
{
	queue<int>q;
	q.push(1);
	int pap, son;
	while (!q.empty())
	{
		pap = q.front(); q.pop();
		for (int i = 0; i<26; i++)if (nxt[pap][i])
		{
			son = nxt[pap][i];
			fail[son] = getfail(fail[pap], i);
			q.push(son);
			cnt[son] += cnt[fail[son]];
			b.v[pap][son] = cnt[son];
		}
		else
		{
			int fl = getfail(fail[pap], i);
			if(fl)b.v[pap][fl] = cnt[fl];
			else b.v[pap][1] = 0;
		}
	}
}
void clr()
{
	memset(nxt, 0, (id + 2) * 4 * 26);
	memset(fail, 0, (id + 2) * 4);
	memset(cnt, 0, (id + 2) * 4);
	id = 1;
}
int n; LL m;
int v[N];
int main()
{
	while (~scanf("%d%lld", &n, &m))
	{
		a.F(); a.v[1][1] = 0;
		b.F();

		clr();
		for (int i = 1; i <= n; ++i)scanf("%d", &v[i]);
		for (int i = 1; i <= n; ++i)
		{
			scanf("%s", s); insert(s, v[i]);
		}
		AC_automation();

		c = a*(b ^ m);
		LL ans = 0;
		for (int i = 1; i <= id; ++i)gmax(ans,c.v[1][i]);
		printf("%lld\n", ans);
	}
	return 0;
}
/*
【trick&&吐槽】
這道題使得我對AC自動機又多了一層的瞭解。

【題意】
我們有n(200)個特殊單詞,總長度不超過200。
我們要構造一個長度為l(1e14)的字串。
如果字串每包含一個給定單詞i(可重疊匹配),則我們的權值計數+a[i]
問你最大可以得到的權值

【型別】
AC自動機+矩陣快速冪

【分析】
首先,因為涉及到字串的轉移,所以我們會首先想到AC自動機。
AC自動機的節點數不會超過200,每個點可以從fail指標中傳來cnt的累積量。
由AC自動機父節點向子節點轉移的時候,我們會獲得子節點cnt的增益。
轉移除了"父節點->子節點"以外,還有"節點->fail節點的轉移",

fail節點的轉移如何實現的呢?
比如當前AC自動機節點代表的字串為abcd,然後,沒有一個為abcde的節點,
在這種情況下,如果我們擴充套件'e',就需要走到"abcde"的fail節點,
比如我們有字串節點表示"cde","abcde"的fail節點,那麼,這個轉移是有意義的,我們會獲得"cde"的權值。

參照程式碼,就是——
pap = q.front(); q.pop();
for (int i = 0; i<26; i++)if (nxt[pap][i])
{
	son = nxt[pap][i];
	fail[son] = getfail(fail[pap], i);
	q.push(son);
	cnt[son] += cnt[fail[son]];
	b.v[pap][son] = cnt[son];
}
else
{
	int fl = getfail(fail[pap], i);
	if(fl)b.v[pap][fl] = cnt[fl];
	else b.v[pap][1] = 0;
}

我們求出了節點之間的轉移情況之後,就有了轉移DP關係。
但是我們需要構建的字串太長了,於是,這個DP關係需要用快速冪來求解。

但是這個快速冪有點特殊。
因為轉移方程是這樣子的,f[nxt]=max(f[pre])+val[nxt]
是一個取max的轉移關係。

於是,轉移部分的程式碼就要是這個樣子——
for (int i = 0; i<G; i++)
{
	for (int j = 0; j<G; j++)
	{
		c.v[i][j] = -1e18;
		for (int k = 0; k<G; k++)
		{
			gmax(c.v[i][j], v[i][k] + b.v[k][j]);
		}
	}
}
在定義轉移關係初始陣列的時候,也要把其權值設定為極小值(對於一般的乘法快速冪矩陣,權值初始是設定為0的)
有一點需要特別注意的是,在我們做矩陣快速冪的冪運算時,y的初值不能全部設為最小,對角線的權值要為0。
因為這裡的y代表的是轉移n步之後額外獲得的權值增量,起點是斜對角上的所有點,故而初始權值這樣設定。
經過之前所有步驟,我們就可以順利AC啦。

【時間複雜度&&優化】
O(|字串總長|^3)

【資料】
1 3
100
s
*/


相關文章