P4022 [CTSC2012] 熟悉的文章
容易发现,能和 SAM 搞在一起的东西还挺多的。
首先要求最大的 \(L\),显然这个 \(L\) 是满足单调性的:若 \(L\) 合法,则 \(L-1,L-2,\dots\) 都合法。所以考虑二分这个 \(L\)。
首先看到这个序列分段,应该想到经典的 DP:设 \(f_i\) 表示到第 \(i\) 个位置最长能匹配多长的字符串,那么显然有转移方程
\[f_i=\max\{f_j+i-j\}~,\quad j\in[i-w_i,i-L]
\]
其中 \(w_i\) 是以 \(i\) 为结尾的字符串出现在模板串中的最长长度。用 SAM 处理这个东西应该是基操,考虑到题目中给出了多个模板串,所以套广义 SAM 的板子就可以处理出它。
现在复杂度是 \(O(n^2)\) 的,但是注意到随着 \(i\) 的增加,\(j\) 的合法取值范围是一个滑动窗口,可以采用单调队列优化,复杂度变为 \(O(n)\)。
#include<bits/stdc++.h>
using namespace std;constexpr int MAXN=2.2e6+5;
int N,M,n,w[MAXN];
struct{int tot=1;struct Trie{int fa,c,s[2];}t[MAXN];void ins(const string&s){int p=1;for(int c:s){c-='0';if(!t[p].s[c]){t[p].s[c]=++tot;t[tot].fa=p,t[tot].c=c;}p=t[p].s[c];}}
}T;
struct{int tot=1;struct SAM{int len,fa,s[2];}sam[MAXN];int ins(int x,int lst){sam[++tot].len=sam[lst].len+1;int pos=lst,ch=T.t[x].c;lst=tot;while(pos&&!sam[pos].s[ch]){sam[pos].s[ch]=tot;pos=sam[pos].fa;}if(!pos) sam[tot].fa=1;else{int p=pos,q=sam[pos].s[ch];if(sam[p].len+1==sam[q].len) sam[tot].fa=q;else{sam[++tot]=sam[q];sam[tot].len=sam[p].len+1;sam[q].fa=sam[lst].fa=tot;while(pos&&sam[pos].s[ch]==q){sam[pos].s[ch]=tot;pos=sam[pos].fa;}}}return lst;}void build(){queue<pair<int,int>>q;for(int i=0;i<2;i++) if(T.t[1].s[i]) q.emplace(T.t[1].s[i],1);while(!q.empty()){int u=q.front().first,lst=q.front().second;q.pop();int now=ins(u,lst);for(int i=0;i<2;i++) if(T.t[u].s[i]) q.emplace(T.t[u].s[i],now);}}void initw(const string&s){int lst=0,pos=1;for(int i=1;i<=n;i++){int c=s[i]-'0';if(sam[pos].s[c]) lst++,pos=sam[pos].s[c];else{while(pos&&!sam[pos].s[c]) pos=sam[pos].fa;if(!pos) lst=0,pos=1;else lst=sam[pos].len+1,pos=sam[pos].s[c];}w[i]=lst;}}int q[MAXN],f[MAXN];bool check(int x){int h=1,t=0;memset(f,0,x<<2);for(int i=x;i<=n;i++){f[i]=f[i-1];while(h<=t&&f[q[t]]-q[t]<=f[i-x]-i+x) t--;q[++t]=i-x;while(h<=t&&q[h]<i-w[i]) h++;if(h<=t) f[i]=max(f[i],f[q[h]]+i-q[h]);}return n*0.9<=f[n];}
}S;int main(){cin.tie(nullptr)->sync_with_stdio(0);cin>>N>>M;for(int i=1;i<=M;i++){string s;cin>>s;T.ins(s);}S.build();for(int i=1;i<=N;i++){string s;cin>>s;n=s.size();s=' '+s;S.initw(s);int l=0,r=n,ans=0;while(l<=r){int mid=(l+r)>>1;if(S.check(mid)) ans=mid,l=mid+1;else r=mid-1;}cout<<ans<<'\n';}return 0;
}