您的位置:首页 > 其它

BZOJ 4560 [JLoi2016]字符串覆盖

2017-02-14 08:11 218 查看
贪心+KMP

一副区间DP的样子,然而并不是

如果只有两个串,那我们可以枚举它们的先后顺序。对于maxans则让前一个串在尽量前的位置匹配,后一个串在尽量后的位置匹配。对于minans则枚举前一个串匹配的位置,把后一个串放在第一个串开头后面第一个匹配位置。

推广下去就有了四个串的做法。枚举顺序,对于maxans,第i+1个串的开头要么在第i个串结尾后的第一个匹配位置,要么在第i个串内部的最后一个匹配位置。枚举一下状态即可。对于minans,枚举第2个串的匹配位置,则第一个串一定在第2个串之前的最后面的匹配位置,对于第三个串,要么在第2个串内的最前位置,要么在第2个串后的任意位置。对于后者用后缀Min维护即可。

然后就港了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10005
#define cmax(u,v) ((u)<(v)?(u)=(v):0)
#define cmin(u,v) ((u)>(v)?(u)=(v):0)
using namespace std;
namespace runzhe2000
{
const int INF = 1<<30;
bool mat[5]
;
int n, m, len[5], next[5]
, h[5], lef[5]
, rig[5]
, maxans, minans, sufmin
;
char str
, s[5]
;

void KMP_next(char *s, int len, int *next)
{
next[1] = 0; int p = 0;
for(int i = 2; i <= len; i++)
{
for(; s[p+1] != s[i] && p; p = next[p]);
if(s[p+1] == s[i]) ++p;
next[i] = p;
}
}

void KMP_solve(char *s, int len, int *next, bool *mat, int *lef, int *rig)
{
int p = 0;
for(int i = 1; i <= n; i++)
{
for(; str[i] != s[p+1] && p; p = next[p]);
if(str[i] == s[p+1]) p++;
if(p == len)
{
p = next[p];
mat[i-len+1] = 1;
}
else mat[i-len+1] = 0;
}
for(int i = n-len+2; i <= n+1; i++) mat[i] = 0;
p = 0; for(int i = 0; i <= n+1; i++) lef[i] = p, mat[i] ? p=i : 0;
p = 0; for(int i = n+1; i >= 0; i--) rig[i] = p, mat[i] ? p=i : 0;
}

void dfs_make(int nnow, int l, int r, int sum)
{
if(nnow > m){cmax(maxans, sum); return;}
int now = h[nnow];
int nl, nr;
nl = lef[now][r+1];
if(nl >= l && nl > 0)
{
nr = nl + len[now] - 1;
dfs_make(nnow+1, nl, max(nr, r), sum + max(0, len[now] - (r-nl+1)));
}
nl = rig[now][r];
if(nl > 0)
{
nr = nl + len[now] - 1;
dfs_make(nnow+1, nl, nr, sum + len[now]);
}
}
void dfs_init(int now)
{
if(now > m) {dfs_make(1,0,0,0);return;}
for(int i = 1; i <= m; i++)
if(!h[i]) {h[i] = now; dfs_init(now + 1); h[i] = 0;}
}

void main()
{
int T; scanf("%d",&T);
for(; T--; )
{
maxans = 0; minans = INF;
memset(h,0,sizeof h);
scanf("%s%d",str + 1,&m);
n = strlen(str + 1);
for(int i = 1; i <= m; i++)
{
scanf("%s",s[i] + 1);
len[i] = strlen(s[i] + 1);
KMP_next(s[i], len[i], next[i]);
KMP_solve(s[i], len[i], next[i], mat[i], lef[i], rig[i]);
}
dfs_init(1);

if(m == 1) minans = len[1];
else if(m == 2)
{
for(int i = 1; i <= n; i++)
for(int j = 1; j <= 2; j++)
if(mat[j][i] && rig[3-j][i-1])
{
int tmp = len[j] + len[3-j] - max(0, min(i + len[j] - rig[3-j][i-1], len[3-j]));
cmin(minans, tmp);
}
}
else if(m == 3)
{
for(int i = 1; i <= 3; i++)
for(int j = 1; j <= 3; j++) if(i != j)
for(int k = 1; k <= 3; k++) if(j != k && i != k)
for(int p = 1; p <= n; p++)
if(mat[j][p] && lef[i][p+1] && rig[k][p-1])
{
int r = p + len[j] - 1, q = lef[i][p+1], g = rig[k][p-1];
int tmp = len[i] + len[j] - max(0, min(r, q+len[i]-1) - p + 1);
r = max(r, q+len[i] - 1);
tmp = tmp + len[k] - max(0, min(r, g+len[k]-1) - g + 1);
cmin(minans, tmp);
}
}
else
{
for(h[1] = 1; h[1] <= 4; h[1]++)
for(h[2] = 1; h[2] <= 4; h[2]++) if(h[1] != h[2])
for(h[3] = 1; h[3] <= 4; h[3]++) if(h[1] != h[3] && h[2]!=h[3])
for(h[4] = 1; h[4] <= 4; h[4]++) if(h[1] != h[4] && h[2] != h[4] && h[3] != h[4])
{
int cur = INF;
for(int i = n+1; i >= 1; i--)
{
if(mat[h[3]][i] && rig[h[4]][i-1])
{
int p = rig[h[4]][i-1];
int tmp = len[h[3]] + len[h[4]] - max(0, min(i+len[h[3]]-1, p+len[h[4]]-1) - p + 1);
cmin(cur, tmp);
}
sufmin[i] = cur;
}
for(int i = 1; i <= n; i++)
if(mat[h[2]][i] && lef[h[1]][i+1])
{
int p = lef[h[1]][i+1], r = max(i+len[h[2]]-1, p+len[h[1]]-1);
int tmp = len[h[1]] + len[h[2]] - max(0, min(i+len[h[2]]-1, p+len[h[1]]-1) - i + 1);
int q = rig[h[3]][i-1];
if(q <= r)
{
int ttmp = tmp + len[h[3]] - (min(r, q + len[h[3]] - 1) - q + 1), tr = max(r, q + len[h[3]] - 1);
int g = rig[h[4]][q-1];
if(g)
{
ttmp = ttmp + len[h[4]] - max(0, min(tr, g + len[h[4]] - 1) - g + 1);
cmin(minans, ttmp);
}
}
tmp = tmp + sufmin[r+1];
cmin(minans, tmp);
}
}
}
printf("%d %d\n",minans, maxans);
}
}
}
int main()
{
runzhe2000::main();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: