【[SDOI2008]Sandy的卡片】
阿新 • • 發佈:2019-01-01
string while 轉化 san register iostream include 字符 ret
被\(mhr\)的暴力幹翻了
這道題做法還是非常好想的
先做一遍差分,在每個串的某尾插入一個特殊字符,再將所有的串拼接在一起
現在的問題就轉化為找到一個最長的公共子串使得其出現了\(n\)次,但是在一個串內出現多次出現只算一次
先考慮一下沒有第二個限制的做法
那就是最簡單的\(SA\)+二分了,就是掃一遍\(height\)數組,之後根據\(height\)進行分組使得一個組內所有的\(height\)都大於等於當前二分出來的\(mid\),之後一個組內有多少個就代表這個子串出現了多少次
現在有了限制,就是不能來自於同一個串
於是我們給每一個\(sa_i\)打一個標記,標記好其來自哪一個數組,之後對於一個組內出現的同一個串內的我們只需要算一次就夠了
代碼
#include<iostream> #include<cstring> #include<cstdio> #define re register #define LL long long #define maxn (1000*100+1000+5)*2 #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define pt putchar(1) inline int read() { char c=getchar();int x=0; while(c<‘0‘||c>‘9‘) c=getchar(); while(c>=‘0‘&&c<=‘9‘) x=(x<<3)+(x<<1)+c-48,c=getchar();return x; } int a[maxn],f[maxn],c[maxn]; int tp[maxn],sa[maxn],rk[maxn],tax[maxn],het[maxn],b[maxn]; int n,m,K,r,t; inline void qsort() { for(re int i=0;i<=K;i++) tax[i]=0; for(re int i=1;i<=n;i++) tax[rk[i]]++; for(re int i=1;i<=K;i++) tax[i]+=tax[i-1]; for(re int i=n;i;--i) sa[tax[rk[tp[i]]]--]=tp[i]; } inline int check(int x) { int num=0,now=1; if(f[sa[1]]) num++,b[f[sa[1]]]++; for(re int i=2;i<=n;i++) { if(het[i]<x) { if(num>=m) return 1; for(re int j=now;j<i;j++) b[f[sa[j]]]=0; num=0,now=i; if(f[sa[i]]) num++,b[f[sa[i]]]++; continue; } if(!b[f[sa[i]]]&&f[sa[i]]) b[f[sa[i]]]++,num++; } return num>=m; } int main() { m=read(); for(re int i=1;i<=m;i++) { t=read();r=max(t,r); for(re int j=1;j<=t;j++) a[++n]=read(),f[n]=i; a[++n]=i+101,f[n]=0; } for(re int i=1;i<=n;i++) c[i]=a[i]-a[i-1]; for(re int i=1;i<=n;i++) c[i]+=2001,K=max(c[i],K); c[1]=0; for(re int i=1;i<=n;i++) tp[i]=i,rk[i]=c[i]; qsort(); for(re int w=1,p=0;p<n;K=p,w<<=1) { p=0; for(re int i=1;i<=w;i++) tp[++p]=n-w+i; for(re int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w; qsort(); for(re int i=1;i<=n;i++) std::swap(rk[i],tp[i]); rk[sa[1]]=p=1; for(re int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p; } int k=0; for(re int i=1;i<=n;i++) { if(k) --k; int j=sa[rk[i]-1]; while(c[i+k]==c[j+k]) ++k; het[rk[i]]=k; } int l=1,ans=0; while(l<=r) { int mid=l+r>>1; if(check(mid)) l=mid+1,ans=mid; else r=mid-1; } printf("%d\n",ans+1); return 0; }
【[SDOI2008]Sandy的卡片】