洛谷P4022熟悉的文章
Posted stoorz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷P4022熟悉的文章相关的知识,希望对你有一定的参考价值。
题目
题目链接:https://www.luogu.com.cn/problem/P4022
阿米巴是小强的好朋友。
在小强眼中,阿米巴是一个作文成绩很高的文艺青年。为了获取考试作文的真谛,小强向阿米巴求教。阿米巴给小强展示了几篇作文,小强觉得这些文章怎么看怎么觉得熟悉,仿佛是某些范文拼拼凑凑而成的。小强不禁向阿米巴投去了疑惑的眼光,却发现阿米巴露出了一个狡黠的微笑。
为了有说服力地向阿米巴展示阿米巴的作文是多么让人觉得“眼熟”,小强想出了一个评定作文 “熟悉程度”的量化指标:\\(L_0\\) .小强首先将作文转化成一个 \\(01\\) 串。之后,小强搜集了各路名家的文章,同样分别转化成 \\(01\\) 串后,整理出一个包含了 \\(M\\) 个 \\(01\\) 串的 “ 标准作文库 ”。
小强认为:如果一个 \\(01\\) 串长度不少于 \\(L\\) 且在标准作文库中的某个串里出现过(即,它是标准作文库的某个串的一个 连续子串),那么它是 “ 熟悉 ” 的。对于一篇作文(一个 \\(01\\) 串)\\(A\\),如果能够把 \\(A\\) 分割成若干段子串,其中 “ 熟悉 ” 的子串的长度总和不少于 \\(A\\) 总长度的 \\(90\\%\\),那么称 \\(A\\) 是 “ 熟悉的文章 ”。 \\(L_0\\) 是能够让 \\(A\\) 成为 “ 熟悉的文章 ” 的 所有 \\(L\\) 的最大值 (如果不存在这样的 \\(L\\),那么规定 \\(L_0=0\\))。
举个例子:
小强的作文库里包含了如下 \\(2\\) 个字符串:
10110
000001110
有一篇待考察的作文是:
1011001100
小强计算出这篇作文 \\(L\\) 的最大值是 \\(4\\),因为待考察的作文可以视作 \\(10110+0110+0\\),其中 \\(10110\\) 和 \\(0110\\) 被判定为 “熟悉” 的。而当 \\(L = 5\\) 或是更大的时候,不存在符合题意的分割方法。所以,这篇作文的 \\(L_0 = 4\\)。小强认为阿米巴作文的 \\(L_0\\) 值比其他同学的明显要大。请你帮他验证一下。
输入文件大小不超过 \\(1.1\\times 10^6\\) 字节。
思路
首先肯定二分答案。考虑如果判断一个答案是否可行。
设 \\(f[i]\\) 表示前 \\(i\\) 个字符在目前二分的 \\(L\\) 下,最多能匹配的字符数量。
那么有转移
其中 \\(g[i]\\) 表示第 \\(i\\) 个字符最多往前多少位的子串是可以匹配上的。这个东西可以建出广义 SAM 后对于每一次询问的字符串求一次。
观察到 \\(i-g[i]\\) 是单调不降的,所以可以用一个单调队列优化 dp 转移。
时间复杂度 \\(O(\\sum |s|+\\sum |t|)\\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=2200010;
const double eps=1e-8;
int n,m,f[N],g[N];
char s[N];
deque<int> q;
struct SAM
{
int last,tot,fa[N],len[N],ch[N][2];
SAM() { last=tot=1; }
void insert(int c)
{
int p=last;
if (ch[p][c])
{
int q=ch[p][c];
if (len[q]==len[p]+1) last=q;
else
{
int nq=++tot; last=nq;
len[nq]=len[p]+1; fa[nq]=fa[q];
ch[nq][0]=ch[q][0]; ch[nq][1]=ch[q][1];
fa[q]=nq;
for (;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
else
{
int np=++tot; last=np;
len[np]=len[p]+1;
for (;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
if (!p) fa[np]=1;
else
{
int q=ch[p][c];
if (len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++tot;
len[nq]=len[p]+1; fa[nq]=fa[q];
ch[nq][0]=ch[q][0]; ch[nq][1]=ch[q][1];
fa[np]=fa[q]=nq;
for (;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
}
void match(char *s)
{
int le=strlen(s+1),p=1;
for (int i=1;i<=le;i++)
{
int c=s[i]-48;
if (ch[p][c]) p=ch[p][c],g[i]=g[i-1]+1;
else
{
while (p && !ch[p][c]) p=fa[p];
g[i]=len[p]+1; p=p?ch[p][c]:1;
}
}
}
}sam;
bool check(int mid,int len)
{
while (q.size()) q.pop_back();
for (int i=1;i<=len;i++)
{
f[i]=f[i-1];
while (q.size() && q.front()<i-g[i]) q.pop_front();
if (q.size()) f[i]=max(f[i],f[q.front()]+i-q.front());
if (i-mid+1>=0)
{
int j=i-mid+1;
while (q.size() && f[q.back()]-q.back()<=f[j]-j) q.pop_back();
q.push_back(j);
}
}
return f[len]>(int)(0.9*len-eps);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%s",s+1);
int len=strlen(s+1); sam.last=1;
for (int j=1;j<=len;j++)
sam.insert(s[j]-48);
}
while (n--)
{
scanf("%s",s+1);
sam.match(s);
int len=strlen(s+1),l=1,r=len,mid;
while (l<=r)
{
mid=(l+r)>>1;
if (check(mid,len)) l=mid+1;
else r=mid-1;
}
cout<<l-1<<"\\n";
}
return 0;
}
以上是关于洛谷P4022熟悉的文章的主要内容,如果未能解决你的问题,请参考以下文章
P4022 [CTSC2012]熟悉的文章(广义SAM+决策单调性)
片段(Java) | 机试题+算法思路+考点+代码解析 2023