题解 P4022【[CTSC2012]熟悉的文章】
upd 2021.7.4:更改部分用词.
题意
给你 $m$ 个文本串 $T_{1,2,...,m}$,$n$ 次询问,对于一个字符串 $A$ 满足 $A=A_1A_2A_3...A_k$,其中 $\forall i\in[1,k],|A_i|\ge L$。求 $L_0=\max \{L|(\sum_{i=1}^k|A_i|[\exists j\in[1,m],A_i\text{ in substrings}(T_j)])\ge|A|\times 0.9\}$。
$\sum|A|+\sum|T_i|\le 1.1\times 10^6$。
思路
首先看到 $\max$ 先知道需要二分答案 $L_0$,然后考虑 $\text{check}$ 一个 $L$ 是否能够满足条件。
看到文本分段自然地想到 $\text{dp}$。
我们设 $dp_i$ 表示以 $i$ 作为当前段结尾,最长可以匹配多长的字符串,则有转移 $dp_i=\max(dp_j+i-j,dp_{i-1})(j\in[i-match_i,i-L])$,其中 $match_i$ 为以 $i$ 作为字符串结尾的字符串在文本串中能够匹配的最长长度。
然后处理 $match_i$ 可以开一个广义 $\text{SAM}$ 统计,与 $\text{LCS}$ 一样,这里不多讲解。
现在我们得到了一个 $O(\sum|T|+\sum|A|^2\log |A|)$ 的算法,显然不能通过。
然后我们考虑 $i$ 显然为严格递增的,于是我们可以维护 $dp_j-j$ 的单调队列然后就可以 $O(1)$ 转移了,时间复杂度降至 $O(\sum|T|+\sum|A|\log |A|)$。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{
}
const int N=2.2e6+10;
int n,m,q;
int pos[N],ansi[N];
char str[N];
struct SAM{
struct statu{
int len,link;
int ch[2];
}a[N];
int siz,last;
SAM(){
a[0].len=0;
a[0].link=-1;
last=0;
}
inline void insert(int c){
int cur=++siz;
a[cur].len=a[last].len+1;
int p=last;
while(p!=-1&&!a[p].ch[c]){
a[p].ch[c]=cur;
p=a[p].link;
}
if(~p){
int q=a[p].ch[c];
if(a[p].len+1==a[q].len){
a[cur].link=q;
}else{
int clone=++siz;
a[clone]=a[q];
a[clone].len=a[p].len+1;
while(p!=-1&&a[p].ch[c]==q){
a[p].ch[c]=clone;
p=a[p].link;
}
a[q].link=a[cur].link=clone;
}
}else{
a[cur].link=0;
}
last=cur;
}
inline void insert(){
static char str[N];
for(int k=1;k<=m;k++){
readstr(str);
int len=strlen(str+1);
last=0;
for(int i=1;i<=len;i++){
insert(str[i]-'0');
}
}
}
inline void calc(){
int anss=0;
int p=1;
for(int i=1;i<=n;i++){
int c=str[i]-'0';
if(a[p].ch[c]){
anss++;
p=a[p].ch[c];
}else{
while(p!=-1&&!a[p].ch[c]){
p=a[p].link;
}
if(~p){
anss=a[p].len+1;
p=a[p].ch[c];
}else{
anss=0;
p=1;
}
}
ansi[i]=anss;
}
}
inline bool check(int L){
static int q[N],dp[N];
int head=1,tail=0;
for(int i=1;i<L;i++) dp[i]=0;
for(int i=L;i<=n;i++){
dp[i]=dp[i-1];
while(head<=tail&&dp[q[tail]]-q[tail]<=dp[i-L]-i+L) tail--;
q[++tail]=i-L;
while(head<=tail&&q[head]<i-ansi[i]) head++;
if(head<=tail) dp[i]=max(dp[i],dp[q[head]]+i-q[head]);
}
return n*0.9<=dp[n];
}
}sam;
int main(){
q=read(),m=read();
sam.insert();
while(q--){
n=readstr(str);
sam.calc();
int l=0,r=n;
while(l<=r){
int mid=l+r>>1;
if(sam.check(mid)) l=mid+1;
else r=mid-1;
}
write(l-1);
putc('\n');
}
flush();
return 0;
}
再见 qwq~

浙公网安备 33010602011771号