【Codeforces Round 362 (Div 2)F】【AC自动机+矩阵快速幂】Legen... 长度为l字符串最大能够重复匹配的字符串权值

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
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值