版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
思路
由于可能转调,
现设匹配长度 , ,w就是转调值,但这样并不好处理。
由于是区间加上(减去)一个值,可以考虑差分,差分之后,得到的两两之差,是一定的。
现假定是 与 的差,原数组匹配转为差分数组也就是:
回忆一下, 数组的定义,不难想到就是当 ,可以拓展状态。
那么问题就迎刃而解了,转为二分+后缀数组。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
const int N=2e4+10;
int s[N],wa[N],wb[N],c[N],wv[N],n,sa[N];
inline bool cmp(int *s,int a,int b,int l){return s[a]==s[b]&&s[a+l]==s[b+l];}
void DA(int *s)
{
int m=188,*x=wa,*y=wb,*t;
for(int i=1;i<=n;i++)++c[x[i]=s[i]];
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
for(int j=1,p=0;p<n;j<<=1,m=p)
{
p=0;
for(int i=n-j+1;i<=n;i++)y[++p]=i;
for(int i=1;i<=n;i++)if(sa[i]>j)y[++p]=sa[i]-j;
for(int i=1;i<=n;i++)wv[i]=x[y[i]];
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)++c[wv[i]];
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[wv[i]]--]=y[i];
t=x;x=y;y=t;p=1;x[sa[1]]=1;
for(int i=2;i<=n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p:++p;
}
}
int rk[N],height[N];
void calheight(int *s,int *sa)
{
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=1,k=0,j;i<=n;height[rk[i++]]=k)//k=h[i-1]
for(k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);//for(k=h[i-1]-1>=0?h[i-1]-1:0)
}
bool check(int k)
{
int l,r;l=r=sa[1];
for(int i=2;i<=n;i++)
{
if(height[i]<k)l=r=sa[i];
else
{
if(sa[i]>r)r=sa[i];
if(sa[i]<l)l=sa[i];
if(r-l>k)return true;
}
}
return false;
}
void solve()
{
int l=0,r=n>>1;
while(l<r)
{
int mid=(l+r+1)>>1;
if(check(mid))l=mid;
else r=mid-1;
}
if(l<4)puts("0");
else printf("%d\n",l+1);
}
int main()
{
//freopen("a0.in","r",stdin);
while(scanf("%d",&n)&&n)
{
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)scanf("%d",&s[i]);--n;
for(int i=1;i<=n;i++)s[i]=s[i+1]-s[i]+100;
DA(s);calheight(s,sa),solve();
}
return 0;
}
BZOJ1717
由于是权限题,暂不提供链接。
思路
和上一题差不多。
可以用单调队列维护,也可以二分求解。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
const int N=2e4+10;
int a[N],b[N],wa[N],wb[N],wv[N],c[N],n,m,k,sa[N];
void disc()
{
sort(a+1,a+n+1);m=0;
for(int i=1;i<=n;i++)
if(i==1||a[i]!=a[i-1])a[++m]=a[i];
}
inline int get(int d)
{
int l=1,r=m;
while(l<r)
{
int mid=l+r>>1;
if(a[mid]<d)l=mid+1;
else r=mid;
}
return l;
}
inline bool cmp(int *s,int a,int b,int l){return s[a]==s[b]&&s[a+l]==s[b+l];}
void DA(int *s)
{
int *x=wa,*y=wb,*t;
for(int i=1;i<=n;i++)++c[x[i]=s[i]];
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
for(int k=1,p=0;p<n;k<<=1,m=p)
{
p=0;
for(int i=n-k+1;i<=n;i++)y[++p]=i;
for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k;
for(int i=1;i<=n;i++)wv[i]=x[y[i]];
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)++c[wv[i]];
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[wv[i]]--]=y[i];
t=x;x=y;y=t;p=1;x[sa[1]]=1;
for(int i=2;i<=n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p:++p;
}
}
int rk[N],height[N];
void calheight(int *s,int *sa)
{
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=1,k=0,j;i<=n;height[rk[i++]]=k)
for(k?k--:k=0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);//sa[
}
bool check(int len)
{
int ans=1;
for(int i=2;i<=n;i++)
{
if(height[i]<len)ans=1;
else {++ans;if(ans==k)return true;}
}
return false;
}
int main()
{
while(scanf("%d%d",&n,&k)!=EOF)
{
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)scanf("%d",&b[i]),a[i]=b[i];
disc();for(int i=1;i<=n;i++)b[i]=get(b[i]);
DA(b);calheight(b,sa);
int l=0,r=m;
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid))l=mid;
else r=mid-1;
}
printf("%d\n",l);
}
return 0;
}
思路
把两个字串拼在一起之后,求 数组。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std;
const int N=2e5+10;
char s[N];int wa[N],wb[N],wv[N],sa[N],c[30],rk[N],height[N],n;
inline bool cmp(int *s,int a,int b,int l){return s[a]==s[b]&&s[a+l]==s[b+l];}
void DA(char *s)
{
int m=28;
int *x=wa,*y=wb,*t;
for(int i=1;i<=n;i++)++c[x[i]=s[i]-'a'+1];
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
for(int k=1,p;k<n;k<<=1,m=p)
{
p=0;
for(int i=n-k+1;i<=n;i++)y[++p]=i;
for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k;
for(int i=1;i<=n;i++)wv[i]=x[y[i]];
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)++c[wv[i]];
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[wv[i]]--]=y[i];
t=x;x=y;y=t;p=1;x[sa[1]]=1;
for(int i=2;i<=n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p:++p;
}
}
void calheight(char *s,int *sa)
{
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=1,j,k=0;i<=n;height[rk[i++]]=k)
for(k?k--:k=0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);
}
int main()
{
scanf("%s",s+1);n=strlen(s+1);
++n;s[n]='a'+27;int len=n;
scanf("%s",s+n+1);n=strlen(s+1);int ans=0;
DA(s);calheight(s,sa);
for(int i=2;i<=n;i++)if((sa[i-1]<len&&sa[i]>len)||(sa[i-1]>len&&sa[i]<len))ans=max(ans,height[i]);
printf("%d\n",ans);
return 0;
}
思路
和上一题差不了多少,把字串拼起来之后,二分判断 数组即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
const int N=1e5+10;
int wa[N],wb[N],wv[N],c[N],sa[N],a[N],rk[N],height[N],n;
inline bool cmp(int *s,int a,int b,int l){return s[a]==s[b]&&s[a+l]==s[b+l];}
void DA(int *s)
{
int *x=wa,*y=wb,*t,m=310;
for(int i=1;i<=n;i++)++c[x[i]=s[i]];
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
for(int k=1,p;p<n;k<<=1,m=p)
{
p=0;
for(int i=n-k+1;i<=n;i++)y[++p]=i;//第二关键字排序
for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k;//y[i]表示排名为i的第二关键字属于哪个后缀
for(int i=1;i<=n;i++)wv[i]=x[y[i]];//第一关键字排序。
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)++c[wv[i]];
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[wv[i]]--]=y[i];
t=x;x=y;y=t;p=1;x[sa[1]]=1;
for(int i=2;i<=n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p:++p;
}
}
int len[105],id[N],cnt,ans[1005],t,ansl;bool v[105];
void calheight(int *s,int *sa)
{
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=1,k=0,j;i<=n;height[rk[i++]]=k)
for(k?k--:k=0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);
for(int i=1,j=1;i<=cnt;i++)
{
for(int k=j;k<=j+len[i]-1;k++)id[rk[k]]=i;
j+=len[i]+1;
}
}
bool check(int x)
{
memset(v,1,sizeof(v));
int tot=0,size=0;bool bk=0;
for(int i=1;i<=n;i++)
{
if(height[i]<x)
{
if(tot>(t>>1))ans[++size]=sa[i-1],bk=1;
memset(v,1,sizeof(v));tot=0;
}
else
{
if(v[id[i]])v[id[i]]=0,++tot;
if(v[id[i-1]])v[id[i-1]]=0,++tot;
}
}
if(tot>(t>>1))ans[++size]=sa[n],bk=1;
if(size)ans[0]=size,ansl=x;
return bk;
}
char s[1005];
int main()
{
bool bk=1;
while(scanf("%d",&t)&&t)
{
n=0;cnt=t;memset(c,0,sizeof(c));
bk?bk=0:puts("");
if(t==1){scanf("%s",s+1);printf("%s\n",s+1);continue;}
for(int i=1;i<=t;i++)
{
scanf("%s",s+1);len[i]=strlen(s+1);
for(int j=1;j<=len[i];j++)a[++n]=s[j];
a[++n]='z'+i;
}
DA(a);
calheight(a,sa);
int l=0,r=n;ans[0]=ansl=0;
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid))l=mid;
else r=mid-1;
}
if(!ans[0]){puts("?");continue;}
for(int i=1;i<=ans[0];i++)
{
for(int j=ans[i];j<ans[i]+ansl;j++)printf("%c",a[j]);
puts("");
}
}
return 0;
}