【[CTSC2012]熟悉的文章】
阿新 • • 發佈:2019-01-05
好題啊
\(SAM\)+單調佇列優化\(dp\)
首先這個\(L\)滿足單調性真是非常顯然我們可以直接二分
二分之後套一個\(dp\)就好了
設\(dp[i]\)表示到達\(i\)位置熟悉的文章的最大長度
有一個非常顯然的\(dp\)方程
\[dp_i=max\{dp_j+i-j\}\ (i-j>=mid)\]
同時\([j+1,i]\)這個子串也得是模式串裡的一個子串
對於上面那個\(dp\)方程,我們把\(i\)提出來,用單調佇列維護一下\(dp_j-j\)的最大值就好了
下面這個限制條件的話,我們可以處理出以\(i\)這個位置的為結尾的和所有的模式串的最長公共子串的長度\(mx[i]\)
這個處理的話我們直接在\(SAM\)上過一遍就好了
之後我們就有了第二個限制
\[j>=i-mx[i]\]
經過分析我們可以發現\(i\)每次穩定加\(1\),而\(mx[i]\)每次最多加\(1\),所以\(i-mx[i]\)是單調不降的的
於是我們還是可以用單調佇列來維護
複雜度\(O(Tnlogn)\)
程式碼
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define maxn 2000010 #define re register #define LL long long #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) char S[maxn]; int fa[maxn<<1],len[maxn<<1],son[maxn<<1][3]; int mx[maxn]; int q[maxn],dp[maxn]; int T,m,n,cnt=1,lst=1; inline void ins(int c) { int f=lst,p=++cnt; lst=p; len[p]=len[f]+1; while(f&&!son[f][c]) son[f][c]=p,f=fa[f]; if(!f) {fa[p]=1;return;} int x=son[f][c]; if(len[f]+1==len[x]) {fa[p]=x;return;} int y=++cnt; len[y]=len[f]+1,fa[y]=fa[x],fa[x]=fa[p]=y; for(re int i=0;i<3;i++) son[y][i]=son[x][i]; while(f&&son[f][c]==x) son[f][c]=y,f=fa[f]; } inline void query() { int now=1,L=0; for(re int i=1;i<=n;i++) { if(son[now][S[i]-'0']) {mx[i]=++L;now=son[now][S[i]-'0'];continue;} while(now&&!son[now][S[i]-'0']) now=fa[now]; if(!now) {mx[i]=L=0;now=1;continue;} L=len[now]+1,mx[i]=L;now=son[now][S[i]-'0']; } } inline int check(int mid) { int h=1,t=0; for(re int i=0;i<=n;i++) q[i]=dp[i]=0; for(re int i=mid;i<=n;i++) { while(h<=t&&dp[i-mid]-i+mid>dp[q[t]]-q[t]) t--; q[++t]=i-mid; while(h<=t&&i-mx[i]>q[h]) h++; if(h<=t) dp[i]=i+dp[q[h]]-q[h]; dp[i]=max(dp[i],dp[i-1]); } if(dp[n]*10>=n*9) return 1; return 0; } int main() { scanf("%d%d",&T,&m); for(re int i=1;i<=m;i++) { scanf("%s",S+1); int L=strlen(S+1); for(re int j=1;j<=L;j++) ins(S[j]-'0'); ins(2); } while(T--) { scanf("%s",S+1),n=strlen(S+1); query(); int l=1,r=n,ans=0; while(l<=r) { int mid=l+r>>1; if(check(mid)) ans=mid,l=mid+1;else r=mid-1; } printf("%d\n",ans); } return 0; }