后缀数组14题小结:
http://poj.org/problem?id=1226
题目大意:给出n个字符串,找出一个最长的字符串,使得它本身或它的反串在所有字符串中都出现过,多组数据。
分析:水题一道,把所有的串正反接一遍,中间用没出现过的字符隔开,然后跑一遍后缀数组。最后二分答案+height分块,记录每一块是否覆盖所有串即可,O(n*log(n))。
其实KMP也可以做,枚举其中最短的那个串的子串,跟其他所有串跑一遍KMP即可。
CODE(suffix array):
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int maxl=21000;
const int maxn=110;
const int cur=400;
int s[maxl];
int len;
int cnt[maxl];
int temp1[maxl],temp2[maxl];
int *x,*y;
int sa[maxl];
int height[maxl];
int id[maxl];
bool vis[maxn];
int t,n;
int Comp(int *y,int a,int b,int l)
{
return y[a]==y[b] && y[a+l]==y[b+l];
}
void Da()
{
int m=1000;
x=temp1,y=temp2;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<len; i++) cnt[ x[i]=s[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=len-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<len; j<<=1,m=p)
{
p=0;
for (int i=len-j; i<len; i++) y[ p++ ]=i;
for (int i=0; i<len; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<len; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=len-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
swap(x,y);
p=1;
x[ sa[0] ]=0;
for (int i=1; i<len; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
int k=0;
height[ x[0] ]=0;
for (int i=0; i<len; i++)
{
if (k) k--;
if (!x[i]) continue;
int j=sa[ x[i]-1 ];
while (s[i+k]==s[j+k]) k++;
height[ x[i] ]=k;
}
/*for (int i=0; i<len; i++) printf("%d ",x[i]);
printf("\n");
for (int i=0; i<len; i++) printf("%d ",sa[i]);
printf("\n");
for (int i=0; i<len; i++) printf("%d ",height[i]);
printf("\n");*/
}
bool Judge(int mid)
{
for (int i=1; i<=n; i++) vis[i]=false;
int num=0;
int last=0;
for (int i=0; i<len; i++)
{
if (height[i]<mid)
{
for (int j=last+1; j<=i; j++)
{
vis[ id[ sa[j] ] ]=false;
int k=sa[j-1];
if (k>=0) vis[ id[k] ]=false;
}
num=0;
last=i;
}
else
{
int isi=id[ sa[i] ];
if ( !vis[isi] && isi )
{
vis[isi]=true;
num++;
}
int j=sa[i-1];
if (j>=0)
{
if ( !vis[ id[j] ] && id[j] )
{
vis[ id[j] ]=true;
num++;
}
}
if (num>=n) return true;
}
}
return false;
}
int Binary()
{
int L=0,R=200;
while (L+1<R)
{
int mid=(L+R)>>1;
if ( Judge(mid) ) L=mid;
else R=mid;
}
return L;
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d",&t);
for (int q=1; q<=t; q++)
{
scanf("%d",&n);
getchar();
len=0;
for (int i=1; i<=n; i++)
{
int L=len;
char c=getchar();
while ( c!='\n' )
{
s[ len ]=c;
id[len]=i;
s[ len++ ]+=cur;
c=getchar();
}
int R=len-1;
id[len]=0;
s[ len++ ]=i+200;
for (int j=R; j>=L; j--)
{
id[len]=i;
s[ len++ ]=s[j];
}
id[len]=0;
s[ len++ ]=n-i;
}
/*for (int i=0; i<len; i++) printf("%d ",s[i]);
printf("\n");
for (int i=0; i<len; i++) printf("%d ",id[i]);
printf("\n");*/
Da();
int ans=Binary();
printf("%d\n",ans);
}
return 0;
}
CODE(KMP):
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int maxn=110;
char s[maxn][maxn];
char r[maxn][maxn];
char temp[maxn];
int len[maxn];
char ans[maxn];
bool f[maxn];
int Next[maxn];
int t,n;
int Ans;
void Swap(char *p,char *q)
{
for (int i=0; i<maxn; i++) temp[i]=p[i];
for (int i=0; i<maxn; i++) p[i]=q[i];
for (int i=0; i<maxn; i++) q[i]=temp[i];
}
void Preparation()
{
int min_s=0;
int min_len=10000001;
for (int i=1; i<=n; i++)
if (len[i]<min_len)
{
min_len=len[i];
min_s=i;
}
Swap(s[1],s[min_s]);
Swap(r[1],r[min_s]);
swap(len[1],len[min_s]);
}
void Make_next(int L)
{
Next[0]=Next[1]=0;
int k=0;
for (int i=2; i<=L; i++)
{
while ( k && ans[k]!=ans[i-1] ) k=Next[k];
if (ans[k]==ans[i-1]) k++;
Next[i]=k;
}
}
void Work(char *q,int L,int ans_len,bool *F)
{
int k=0;
for (int i=0; i<L; i++)
{
while ( k && ans[k]!=q[i] ) k=Next[k];
if ( ans[k]==q[i] ) k++;
if (k==ans_len)
{
*F=true;
break;
}
}
}
void Do(char *q)
{
for (int L=len[1]; L>=1; L--)
for (int i=0; i<=len[1]-L; i++)
{
for (int j=0; j<L; j++) ans[j]=q[i+j];
Make_next(L);
int j;
for (j=2; j<=n; j++)
{
f[j]=false;
Work(s[j],len[j],L,&f[j]);
if (!f[j]) Work(r[j],len[j],L,&f[j]);
if (!f[j]) break;
}
if (j!=n+1) continue;
Ans=max(Ans,L);
break;
}
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d",&t);
for (int q=1; q<=t; q++)
{
Ans=0;
scanf("%d",&n);
for (int i=1; i<=n; i++)
{
scanf("%s",&s[i]);
len[i]=strlen(s[i]);
for (int j=0; j<len[i]; j++) r[i][j]=s[i][len[i]-j-1];
}
Preparation();
Do(s[1]);
Do(r[1]);
printf("%d\n",Ans);
}
return 0;
}
http://poj.org/problem?id=1743
题目大意:给出一个串,求其中最长的一个子串,使得它本身或它每一位+x在原串中出现了两次以上,并且不重叠。
分析:水题一道,求出每两位之间的差,跑后缀数组,二分答案+height分块,每一块看sa的最大值减最小值是否>=len即可。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=20110;
int a[maxn];
int cnt[maxn];
int temp1[maxn],temp2[maxn];
int *x=temp1,*y=temp2;
int sa[maxn];
int height[maxn];
int n;
int Comp(int *r,int a,int b,int l)
{
return r[a]==r[b] && r[a+l]==r[b+l];
}
void Da()
{
int m=200;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) y[ p++ ]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
p=1;
swap(x,y);
x[ sa[0] ]=0;
for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
void Calc_height()
{
height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!x[i]) continue;
int j=sa[ x[i]-1 ];
while (a[i+k]==a[j+k]) k++;
height[ x[i] ]=k;
}
}
bool Judge(int z)
{
int low,high;
for (int i=0; i<n; i++)
{
if (height[i]<z)
{
low=20001;
high=0;
}
else
{
low=min(low,sa[i]);
high=max(high,sa[i]);
low=min(low,sa[i-1]);
high=max(high,sa[i-1]);
if (low+z<high) return true;
}
}
return false;
}
int Binary()
{
int L=0,R=20001;
while (L+1<R)
{
int mid=(L+R)>>1;
if ( Judge(mid) ) L=mid;
else R=mid;
}
return L;
}
int main()
{
freopen("c.in","r",stdin);
freopen("my.out","w",stdout);
while ( 1 )
{
scanf("%d",&n);
if (!n) break;
for (int i=0; i<n; i++) scanf("%d",&a[i]);
for (int i=1; i<n; i++) a[i-1]=a[i]-a[i-1];
for (int i=0; i<n; i++) a[i]+=100;
a[n-1]=0;
//for (int i=0; i<n; i++) printf("%d ",a[i]);
//printf("\n");
Da();
Calc_height();
int ans=Binary();
if (ans>=4) printf("%d\n",ans+1);
else printf("0\n");
//for (int i=0; i<n; i++) printf("%d ",sa[i]);
//printf("\n");
//for (int i=0; i<n; i++) printf("%d ",height[i]);
//printf("\n");
}
return 0;
}
http://poj.org/problem?id=2406
题目大意:给一个字符串,求一个最大的t,使得原串是某个串刚好复制t次得到的。
分析:先讲KMP的做法:若n-next[n]是n的因数则为n/(n-next[n]),否则为1。这个很好证明。
至于后缀数组,本质上也差不多,就是求一个最小的k使得lcp(suffix(0),suffix(k))==n-k。
CODE(KMP):
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1000100;
int Next[maxn];
char s[maxn];
int len;
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
while ( scanf("%s",&s),s[0]!='.' )
{
len=strlen(s);
int k=0;
Next[0]=Next[1]=0;
for (int i=2; i<=len; i++)
{
while ( k && s[k]!=s[i-1] ) k=Next[k];
if ( s[k]==s[i-1] ) k++;
Next[i]=k;
}
//for (int i=0; i<=len; i++) printf("%d ",Next[i]);
//printf("\n");
int ans=1;
if ( len%(len-Next[len])==0 ) ans=len/(len-Next[len]);
printf("%d\n",ans);
}
return 0;
}
CODE(suffix array)(我超时了,因为我不会DC3……):
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1000100;
char s[maxn];
int a[maxn];
int cnt[maxn];
int temp1[maxn],temp2[maxn];
int *x=temp1,*y=temp2;
int sa[maxn];
int height[maxn];
bool f[maxn];
int num;
int Comp(int *r,int a,int b,int l)
{
return r[a]==r[b] && r[a+l]==r[b+l];
}
void Da(int n)
{
int m=30;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) y[ p++ ]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
p=1;
swap(x,y);
x[ sa[0] ]=0;
for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
void Calc_height(int n)
{
height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!x[i]) continue;
int j=sa[ x[i]-1 ];
while (a[i+k]==a[j+k]) k++;
height[ x[i] ]=k;
}
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
while ( scanf("%s",&s) && s[0]!='.' )
{
int len=strlen(s);
for (int i=0; i<len; i++) a[i]=s[i]-'a'+1;
a[ len++ ]=0;
Da(len);
Calc_height(len);
len--;
int min_k=len,lcp=len;
for (int i=x[0]+1; i<=len; i++)
{
lcp=min(lcp,height[i]);
int k=sa[i];
if ( lcp==len-k && len%k==0 ) min_k=min(min_k,k);
}
int ans=len/min_k;
min_k=len,lcp=len;
for (int i=x[0]; i>=0; i--)
{
lcp=min(lcp,height[i]);
int k=sa[i-1];
if ( lcp==len-k && len%k==0 ) min_k=min(min_k,k);
}
ans=max(ans,len/min_k);
printf("%d\n",ans);
}
return 0;
}
http://poj.org/problem?id=2774
题目大意:两个串的最长公共子串。
分析:裸题一道……
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=200100;
char s[maxn];
int a[maxn];
int id[maxn];
int cnt[maxn];
int temp1[maxn],temp2[maxn];
int *x=temp1,*y=temp2;
int sa[maxn];
int height[maxn];
int Comp(int *r,int a,int b,int l)
{
return r[a]==r[b] && r[a+l]==r[b+l];
}
void Da(int n,int m)
{
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) y[ p++ ]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
p=1;
swap(x,y);
x[ sa[0] ]=0;
for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
void Calc_height(int n)
{
height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!x[i]) continue;
int j=sa[ x[i]-1 ];
while (a[i+k]==a[j+k]) k++;
height[ x[i] ]=k;
}
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%s",&s);
int len1=strlen(s);
scanf("%s",&s[len1]);
int len2=strlen(s);
for (int i=0; i<len1; i++) a[i]=s[i]-'a'+2;
a[len1]=1;
for (int i=len1; i<len2; i++) a[i+1]=s[i]-'a'+2;
a[len2+1]=0;
len2+=2;
Da(len2,35);
Calc_height(len2);
for (int i=0; i<len1; i++) id[i]=1;
id[len1]=4;
for (int i=len1+1; i<len2-1; i++) id[i]=2;
id[len2-1]=5;
int ans=0;
for (int i=1; i<len2; i++)
if ( id[ sa[i-1] ]+id[ sa[i] ]==3 )
ans=max(ans,height[i]);
printf("%d\n",ans);
return 0;
}
http://poj.org/problem?id=3261
题目大意:求一个最大的k,使得某个子串在原串中出现了k次以上,可重叠。
分析:二分答案,看height数组中是否有>=k的一段height都大于了当前答案。
但我们发现可以枚举任意一个长度为k的区间,用他的最小值去更新答案(很明显,长度更长一定不会优),这是滑动区间最小值问题,单调队列即可。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=20100;
const int maxm=1000100;
int a[maxn];
int cnt[maxm];
int temp1[maxn],temp2[maxn];
int *x=temp1,*y=temp2;
int sa[maxn];
int height[maxn];
struct data
{
int val,Time;
} que[maxn];
int head=1,tail=0;
int n,K;
int m=1000010;
int ans;
int Comp(int *r,int a,int b,int l)
{
return r[a]==r[b] && r[a+l]==r[b+l];
}
void Da()
{
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) y[ p++ ]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
p=1;
swap(x,y);
x[ sa[0] ]=0;
for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
void Calc_height()
{
height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!x[i]) continue;
int j=sa[ x[i]-1 ];
while (a[i+k]==a[j+k]) k++;
height[ x[i] ]=k;
}
}
void Solve()
{
for (int i=0; i<K; i++)
{
que[ ++tail ].val=height[i];
que[tail].Time=i;
while ( tail>1 && que[tail-1].val>que[tail].val )
{
tail--;
que[tail]=que[tail+1];
}
}
ans=que[head].val;
for (int i=K; i<n; i++)
{
que[ ++tail ].val=height[i];
que[tail].Time=i;
while ( tail>head && que[tail-1].val>que[tail].val )
{
tail--;
que[tail]=que[tail+1];
}
if (que[head].Time<=i-K) head++;
ans=max(ans,que[head].val);
}
}
void Print(int *r)
{
for (int i=0; i<n; i++) printf("%d ",r[i]);
printf("\n");
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d%d",&n,&K);
if (K==1)
{
printf("%d\n",n);
return 0;
}
for (int i=0; i<n; i++)
{
scanf("%d",&a[i]);
a[i]++;
}
a[ n++ ]=0;
Da();
Calc_height();
//Print(x);
//Print(sa);
//Print(height);
K--;
Solve();
printf("%d\n",ans);
return 0;
}
http://poj.org/problem?id=3294
题目大意:多个串的最长公共子串,并输出所有串。
分析:本题其实分成了两个任务:求长度+打印答案。前者用二分答案,后者单独做一遍即可。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=101000;
int a[maxn];
char s[1010];
int cnt[maxn];
int temp1[maxn],temp2[maxn];
int *x=temp1,*y=temp2;
int sa[maxn];
int height[maxn];
int id[maxn];
bool f[maxn];
int N,n,m;
int Comp(int *r,int a,int b,int l)
{
return r[a]==r[b] && r[a+l]==r[b+l];
}
void Da()
{
m=500;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) y[ p++ ]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
p=1;
swap(x,y);
x[ sa[0] ]=0;
for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
void Calc_height()
{
height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!x[i]) continue;
int j=sa[ x[i]-1 ];
while (a[i+k]==a[j+k]) k++;
height[ x[i] ]=k;
}
}
bool Judge(int mid)
{
for (int i=0; i<=N; i++) f[i]=false;
int num=0;
int last=0;
for (int i=1; i<n; i++)
if (height[i]<mid)
{
for (int j=last+1; j<i; j++) f[ id[ sa[j] ] ]=f[ id[ sa[j-1] ] ]=false;
last=i;
num=0;
}
else
{
int isi=id[ sa[i] ];
int isi1=id[ sa[i-1] ];
if ( isi && !f[isi] )
{
f[isi]=true;
num++;
}
if ( isi1 && !f[isi1] )
{
f[isi1]=true;
num++;
}
if (2*num>N) return true;
}
return false;
}
int Binary()
{
int L=0,R=1001;
while (L+1<R)
{
int mid=(L+R)>>1;
if ( Judge(mid) ) L=mid;
else R=mid;
}
return L;
}
void Print(int len)
{
for (int i=0; i<=N; i++) f[i]=false;
int num=0;
int last=0;
height[n]=0;
for (int i=1; i<=n; i++)
if (height[i]<len)
{
if (2*num>N)
{
for (int j=0; j<len; j++) printf("%c",a[ sa[i-1]+j ]-200);
printf("\n");
}
for (int j=last+1; j<i; j++) f[ id[ sa[j-1] ] ]=f[ id[ sa[j] ] ]=false;
last=i;
num=0;
}
else
{
int isi=id[ sa[i] ];
int isi1=id[ sa[i-1] ];
if ( isi && !f[isi] )
{
f[isi]=true;
num++;
}
if ( isi1 && !f[isi1] )
{
f[isi1]=true;
num++;
}
}
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
while ( scanf("%d",&N),N )
{
n=0;
for (int i=1; i<=N; i++)
{
scanf("%s",&s);
if (N==1)
{
printf("%s\n",s);
continue;
}
int len=strlen(s);
for (int j=0; j<len; j++)
{
a[n+j]=s[j];
a[n+j]+=200;
id[n+j]=i;
}
n+=len;
a[n]=i;
id[ n++ ]=0;
}
a[n-1]=0;
//for (int i=0; i<n; i++) printf("%d %d\n",a[i],id[i]);
//printf("\n");
Da();
Calc_height();
int len=Binary();
if (N!=1) if (len) Print(len);
else printf("?\n");
printf("\n");
}
return 0;
}
http://poj.org/problem?id=3415
题目大意:给出两个串A,B,求有多少个三元组(I,j,k)使得A从i开始的k位==B从j开始的k位,其中k要大于一个给定的数值x,多组数据。
分析:跑一遍后缀数组,然后我们在做到sa[i]的时候,我们要考虑它和sa[j](1<=j<I且sa[i],sa[j]来自不同的字符串)对答案的贡献。其中k可以取x~lcp(sa[i],sa[j])。
一开始我用了两棵线段树,3000ms,后来发现简直就是个傻叉。由于一个height[i]进栈会不断地把前面那些比它大的值压成和它自己一样的值,我们再分别记录一下当前栈中来自A,B串的总和是多少即可,900ms。
CODE(单调栈):
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=200100;
typedef long long LL;
char s[maxn];
int a[maxn];
int id[maxn];
int cnt[maxn];
int temp1[maxn],temp2[maxn];
int *x=temp1,*y=temp2;
int sa[maxn];
int height[maxn];
struct data
{
LL val;
int num[2];
} sak[maxn];
int tail;
LL sum[2];
int k,n;
LL ans;
int Comp(int *r,int a,int b,int l)
{
return r[a]==r[b] && r[a+l]==r[b+l];
}
void Da(int m)
{
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) y[ p++ ]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
p=1;
swap(x,y);
x[ sa[0] ]=0;
for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
void Calc_height()
{
height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!x[i]) continue;
int j=sa[ x[i]-1 ];
while (a[i+k]==a[j+k]) k++;
height[ x[i] ]=k;
}
}
void Solve()
{
ans=0;
sum[0]=sum[1]=0;
tail=0;
for (int i=2; i<n; i++)
{
int v=height[i]-k+1;
sak[ ++tail ].val=v;
int isi=id[ sa[i-1] ];
sum[isi]+=v;
sak[tail].num[isi]=1;
sak[tail].num[!isi]=0;
while ( tail>1 && sak[tail-1].val>=sak[tail].val )
{
sum[0]-=sak[tail-1].num[0]*(sak[tail-1].val-v);
sum[1]-=sak[tail-1].num[1]*(sak[tail-1].val-v);
sak[tail].num[0]+=sak[tail-1].num[0];
sak[tail].num[1]+=sak[tail-1].num[1];
sak[--tail]=sak[tail+1];
}
if (v<=0)
{
sum[0]-=v*sak[tail].num[0];
sum[1]-=v*sak[tail].num[1];
tail--;
}
ans+=sum[ !id[ sa[i] ] ];
}
cout<<ans<<endl;
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
while ( scanf("%d",&k),k )
{
scanf("%s",&s);
n=strlen(s);
for (int i=0; i<n; i++)
{
a[i]=s[i];
a[i]+=100;
id[i]=0;
}
a[n]=499;
id[ n++ ]=3;
scanf("%s",&s);
int len=strlen(s);
for (int i=0; i<len; i++)
{
a[n+i]=s[i];
a[n+i]+=100;
id[n+i]=1;
}
n+=len;
a[n]=0;
id[ n++ ]=4;
Da(500);
Calc_height();
Solve();
}
return 0;
}
CODE(线段树):
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=200100;
typedef long long LL;
char s[maxn];
int a[maxn];
int id[maxn];
int cnt[maxn];
int temp1[maxn],temp2[maxn];
int *x=temp1,*y=temp2;
int sa[maxn];
int height[maxn];
struct Tnode
{
LL sum;
int add;
bool empty;
} tree[2][maxn<<2];
int k,n;
LL ans;
int Comp(int *r,int a,int b,int l)
{
return r[a]==r[b] && r[a+l]==r[b+l];
}
void Da(int m)
{
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) y[ p++ ]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
p=1;
swap(x,y);
x[ sa[0] ]=0;
for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
void Calc_height()
{
height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!x[i]) continue;
int j=sa[ x[i]-1 ];
while (a[i+k]==a[j+k]) k++;
height[ x[i] ]=k;
}
}
void Build(int root,int L,int R)
{
tree[0][root].sum=tree[0][root].add=0;
tree[0][root].empty=false;
tree[1][root].sum=tree[1][root].add=0;
tree[1][root].empty=false;
if (L==R) return;
int left=root<<1;
int right=left|1;
int mid=(L+R)>>1;
Build(left,L,mid);
Build(right,mid+1,R);
}
void Down(int Id,int root,int L,int R,int left,int right,int mid)
{
if (tree[Id][root].empty)
{
tree[Id][left].sum=tree[Id][left].add=0;
tree[Id][right].sum=tree[Id][right].add=0;
tree[Id][left].empty=tree[Id][right].empty=true;
tree[Id][root].empty=false;
}
if (tree[Id][root].add)
{
tree[Id][left].add+=tree[Id][root].add;
tree[Id][left].sum+=(long long)tree[Id][root].add*(mid-L+1);
tree[Id][right].add+=tree[Id][root].add;
tree[Id][right].sum+=(long long)tree[Id][root].add*(R-mid);
tree[Id][root].add=0;
}
}
void Update(int Id,int root,int L,int R,int x,int y)
{
if ( y<L || R<x ) return;
if ( x<=L && R<=y )
{
tree[Id][root].add++;
tree[Id][root].sum+=(R-L+1);
return;
}
int left=root<<1;
int right=left|1;
int mid=(L+R)>>1;
Down(Id,root,L,R,left,right,mid);
Update(Id,left,L,mid,x,y);
Update(Id,right,mid+1,R,x,y);
tree[Id][root].sum=tree[Id][left].sum+tree[Id][right].sum;
}
LL Query(int Id,int root,int L,int R,int x,int y)
{
if ( y<L || R<x ) return 0;
if ( x<=L && R<=y ) return tree[Id][root].sum;
int left=root<<1;
int right=left|1;
int mid=(L+R)>>1;
Down(Id,root,L,R,left,right,mid);
LL vl=Query(Id,left,L,mid,x,y);
LL vr=Query(Id,right,mid+1,R,x,y);
return vl+vr;
}
void Clear(int root,int L,int R,int x,int y)
{
if ( y<L || R<x ) return;
if ( x<=L && R<=y )
{
tree[0][root].sum=tree[0][root].add=0;
tree[0][root].empty=true;
tree[1][root].sum=tree[1][root].add=0;
tree[1][root].empty=true;
return;
}
int left=root<<1;
int right=left|1;
int mid=(L+R)>>1;
Down(0,root,L,R,left,right,mid);
Down(1,root,L,R,left,right,mid);
Clear(left,L,mid,x,y);
Clear(right,mid+1,R,x,y);
tree[0][root].sum=tree[0][left].sum+tree[0][right].sum;
tree[1][root].sum=tree[1][left].sum+tree[1][right].sum;
}
void Solve()
{
int N=k;
for (int i=0; i<n; i++) N=max(N,height[i]+1);
Build(1,1,N);
LL ans=0;
for (int i=1; i<n; i++)
{
Clear(1,1,N,height[i]+1,N);
int isi=id[ sa[i] ];
if (height[i]>=k) ans+=Query(!isi,1,1,N,k,height[i]);
if ( isi==0 || isi==1 ) Update(isi,1,1,N,k,N);
//for (int j=1; j<=N; j++) cout<<Query(1,1,1,N,j,j)<<' ';
//printf("\n");
}
cout<<ans<<endl;
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
while ( scanf("%d",&k),k )
{
scanf("%s",&s);
n=strlen(s);
for (int i=0; i<n; i++)
{
a[i]=s[i];
a[i]+=100;
id[i]=0;
}
a[n]=50;
id[ n++ ]=3;
scanf("%s",&s);
int len=strlen(s);
for (int i=0; i<len; i++)
{
a[n+i]=s[i];
a[n+i]+=100;
id[n+i]=1;
}
n+=len;
a[n]=0;
id[ n++ ]=4;
Da(500);
Calc_height();
Solve();
}
return 0;
}
http://poj.org/problem?id=3693
题目大意:给出一个串,求它的一个子串,使得这个子串是串t刚好重复k次拼成的,且k最大。如果有多个串输出字典序最小,多组数据。
分析:这题要输出字典序最小好恶心。我们要跑两个后缀数组:一个是正串的,一个是反串的。我们枚举t的长度L,然后检验s[i*L]与s[i*L+L]前后最长能匹配多少,假设最前能匹配到head,长度为len,我们的开头就可以是head~head+(len%L)这段,再用一个rmq求这段sa的最小值即可。In short:两个后缀数组,三个rmq。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=100100;
const int maxl=20;
char s[maxn];
int a1[maxn];
int a2[maxn];
int cnt[maxn];
int temp1[maxn],temp2[maxn];
int *x=temp1,*y=temp2;
int Sa1[maxn],Sa2[maxn];
int Rank1[maxn],Rank2[maxn];
int Height1[maxn],Height2[maxn];
int rmq1[maxn][maxl],rmq2[maxn][maxl],rmq[maxn][maxl];
int P[maxn];
int n,Num=0;
void Make_P()
{
P[1]=0;
for (int i=2; i<maxn; i++) P[i]=P[i/2]+1;
}
int Comp(int *r,int a,int b,int l)
{
return r[a]==r[b] && r[a+l]==r[b+l];
}
void Da(int *sa,int *rank,int *a,int *height,int m)
{
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) y[ p++ ]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
p=1;
swap(x,y);
x[ sa[0] ]=0;
for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
for (int i=0; i<n; i++) rank[ sa[i] ]=i;
height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!rank[i]) continue;
int j=sa[ rank[i]-1 ];
while (a[i+k]==a[j+k]) k++;
height[ rank[i] ]=k;
}
}
void Make_rmq()
{
for (int i=0; i<n; i++)
{
rmq1[i][0]=Height1[i];
rmq2[i][0]=Height2[i];
rmq[i][0]=i;
}
int maxj=P[n]+1;
for (int j=1; j<maxj; j++)
for (int i=0; i<n; i++)
{
int mid=i+(1<<(j-1));
mid=min(mid,n-1);
rmq1[i][j]=min(rmq1[i][j-1],rmq1[mid][j-1]);
rmq2[i][j]=min(rmq2[i][j-1],rmq2[mid][j-1]);
rmq[i][j]=rmq[i][j-1];
if ( Rank1[ rmq[mid][j-1] ]<Rank1[ rmq[i][j] ] ) rmq[i][j]=rmq[mid][j-1];
}
}
int Query1(int i,int j)
{
int p=Rank1[i],q=Rank1[j];
if (p>q) swap(p,q);
p++;
int lg=P[q-p+1];
q=q-(1<<lg)+1;
int len=min(rmq1[p][lg],rmq1[q][lg]);
return len;
}
int Query2(int i,int j)
{
int p=Rank2[i],q=Rank2[j];
if (p>q) swap(p,q);
p++;
int lg=P[q-p+1];
q=q-(1<<lg)+1;
int len=min(rmq2[p][lg],rmq2[q][lg]);
return len;
}
int Query(int p,int q)
{
int lg=P[q-p+1];
q=q-(1<<lg)+1;
int h=rmq[p][lg];
if ( Rank1[ rmq[q][lg] ]<Rank1[h] ) h=rmq[q][lg];
return h;
}
void Solve()
{
int ans=1,ansh=0,ansL=0;//ans==1 special judge
for (int L=1; L<n-1; L++)
for (int i=0; i<n; i+=L)
{
int j=i+L;
if (j>=n-1) continue;
int len1=Query1(i,j);
int len2=Query2(n-i-1,n-j-1);
int len=len1+len2;
int h=i-len2;
int na=len/L+1;
int nh=Query(h,h+len%L);
if ( na>ans || ( na==ans && Rank1[nh]<Rank1[ansh] ) )
{
ans=na;
ansh=nh;
ansL=L;
}
}
if (ans>1)
for (int i=1; i<=ans; i++)
for (int j=0; j<ansL; j++)
printf("%c",s[ansh+j]);
else
{
int c='z';
for (int i=0; i<n-1; i++) c=min(c,(int)s[i]);
printf("%c",c);
}
printf("\n");
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
Make_P();
while ( scanf("%s",&s),s[0]!='#' )
{
Num++;
printf("Case %d: ",Num);
n=strlen(s);
for (int i=0; i<n; i++) a1[i]=s[i]-'a'+1;
for (int i=0; i<n; i++) a2[i]=a1[n-i-1];
a1[n]=0;
a2[ n++ ]=0;
Da(Sa1,Rank1,a1,Height1,27);
Da(Sa2,Rank2,a2,Height2,27);
Make_rmq();
Solve();
}
return 0;
}
http://www.spoj.com/problems/PHRASES/
题目大意:给出n个串,求一个最长的串,使得它在每个串中不重叠地出现了至少2次:
分析:跟poj3294与poj1743差不多,水……
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=100100;
const int oo=100000001;
char s[10010];
int a[maxn];
int id[maxn];
int cnt[maxn];
int temp1[maxn],temp2[maxn];
int *x=temp1,*y=temp2;
int sa[maxn];
int height[maxn];
int low[maxn];
int high[maxn];
int t,N,n,m;
int Comp(int *r,int p,int q,int l)
{
return r[p]==r[q] && r[p+l]==r[q+l];
}
void Da()
{
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) y[ p++ ]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
p=1;
swap(x,y);
x[ sa[0] ]=0;
for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
void Calc_height()
{
height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!x[i]) continue;
int j=sa[ x[i]-1 ];
while (a[i+k]==a[j+k]) k++;
height[ x[i] ]=k;
}
}
bool Judge(int mid)
{
for (int i=1; i<=N; i++)
{
low[i]=oo;
high[i]=-oo;
}
int last=0,num=0;
for (int i=1; i<n; i++)
{
if (height[i]<mid)
{
for (int j=last+1; j<i; j++)
{
int temp=id[ sa[j-1] ];
low[temp]=oo;
high[temp]=-oo;
temp=id[ sa[j] ];
low[temp]=oo;
high[temp]=-oo;
}
last=i;
num=0;
}
else
{
int temp=id[ sa[i-1] ];
if ( high[temp]-low[temp]<mid && temp )
{
high[temp]=max(high[temp],sa[i-1]);
low[temp]=min(low[temp],sa[i-1]);
if (high[temp]-low[temp]>=mid) num++;
}
temp=id[ sa[i] ];
if ( high[temp]-low[temp]<mid && temp )
{
high[temp]=max(high[temp],sa[i]);
low[temp]=min(low[temp],sa[i]);
if (high[temp]-low[temp]>=mid) num++;
}
if (num>=N) return true;
}
}
return false;
}
int Binary()
{
int L=0,R=maxn;
while (L+1<R)
{
int mid=(L+R)>>1;
if ( Judge(mid) ) L=mid;
else R=mid;
}
return L;
}
int main()
{
freopen("c.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d",&t);
while (t--)
{
scanf("%d",&N);
n=0;
for (int i=1; i<=N; i++)
{
scanf("%s",&s);
int len=strlen(s);
while (s[len-1]==' ') len--;
for (int j=0; j<len; j++)
{
a[n+j]=(int)s[j]+50;
id[n+j]=i;
}
n+=len;
a[n]=i;
id[ n++ ]=i+50;
}
a[n-1]=0;
m=500;
Da();
Calc_height();
int ans=Binary();
printf("%d\n",ans);
}
return 0;
}
http://www.spoj.com/problems/REPEATS/
题目大意:跟poj3693一样,不过输出k即可。
分析:这题是poj3693的简化版,不过我们可以考虑一些更简单的做法。我们求出s[i*L]与s[i*L+L]往后能匹配len个之后,我们可以认为它是在前面少了L-len%L个字符,然后我们把开头挪到那里去再搞一次尝试更新答案即可。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=50100;
const int maxl=20;
int a[maxn];
int cnt[maxn];
int temp1[maxn],temp2[maxn];
int *x=temp1,*y=temp2;
int sa[maxn];
int height[maxn];
int lg[maxn];
int rmq[maxn][maxl];
int t,n,m;
void Make_lg()
{
lg[1]=0;
for (int i=2; i<maxn; i++) lg[i]=lg[i/2]+1;
}
int Comp(int *r,int p,int q,int l)
{
return r[p]==r[q] && r[p+l]==r[q+l];
}
void Da()
{
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) y[ p++ ]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
p=1;
swap(x,y);
x[ sa[0] ]=0;
for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
void Calc_height()
{
height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!x[i]) continue;
int j=sa[ x[i]-1 ];
while (a[i+k]==a[j+k]) k++;
height[ x[i] ]=k;
}
}
void Make_rmq()
{
for (int i=0; i<n; i++) rmq[i][0]=height[i];
for (int j=1; j<=lg[n]; j++)
for (int i=0; i<n; i++)
{
int mid=i+(1<<(j-1));
mid=min(mid,n-1);
rmq[i][j]=min(rmq[i][j-1],rmq[mid][j-1]);
}
}
int Query(int i,int j)
{
int p=x[i],q=x[j];
if (p>q) swap(p,q);
p++;
int r=lg[q-p+1];
q=q-(1<<r)+1;
return min(rmq[p][r],rmq[q][r]);
}
void Solve()
{
int ans=1;
for (int L=1; L<n-1; L++)
for (int i=0; i<n; i+=L)
{
int j=i+L;
if (j>=n) break;
int len=Query(i,j);
ans=max(ans,len/L+1);
int ni=i-(L-len%L);
if (ni<0) continue;
j=ni+L;
len=Query(ni,j);
ans=max(ans,len/L+1);
}
printf("%d\n",ans);
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
Make_lg();
scanf("%d",&t);
while (t--)
{
scanf("%d",&n);
for (int i=0; i<n; i++)
{
char c=getchar();
while ( c!='a' && c!='b' ) c=getchar();
a[i]=c-'a'+1;
}
a[ n++ ]=0;
m=3;
Da();
Calc_height();
Make_rmq();
Solve();
}
return 0;
}
http://www.spoj.com/problems/DISUBSTR/
题目大意:给出一个串,求有多少个不同的子串。
分析:水题,用len(sa[i])-height[i]更新答案即可(前提是len(sa[i])>height[i])。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1010;
char s[maxn];
int a[maxn];
int cnt[maxn];
int temp1[maxn],temp2[maxn];
int *x=temp1,*y=temp2;
int sa[maxn];
int height[maxn];
int id[maxn];
int N,n,m;
int Comp(int *r,int a,int b,int l)
{
return r[a]==r[b] && r[a+l]==r[b+l];
}
void Da()
{
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) y[ p++ ]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
p=1;
swap(x,y);
x[ sa[0] ]=0;
for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
void Calc_height()
{
height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!x[i]) continue;
int j=sa[ x[i]-1 ];
while (a[i+k]==a[j+k]) k++;
height[ x[i] ]=k;
}
}
void Solve()
{
for (int i=0; i<n; i++) id[i]=0;
int ans=0,sum=0;
for (int i=n-2; i>=0; i--)
{
sum++;
sum-=id[i+1];
ans+=sum;
id[ height[ x[n-i-2] ] ]++;
}
printf("%d\n",ans);
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d",&N);
while (N--)
{
scanf("%s",&s);
n=strlen(s);
for (int i=0; i<n; i++) a[i]=(int)s[i]+1;
a[ n++ ]=0;
m=500;
Da();
Calc_height();
Solve();
}
return 0;
}
http://www.spoj.com/problems/SUBST1/
和上面那题一毛一样……
Timus Online Judge 1517:
题目大意:两个串的最长公共子串,并输出字典序最小。
分析:水……
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=200100;
char s[maxn];
int a[maxn];
int id[maxn];
int cnt[maxn];
int temp1[maxn],temp2[maxn];
int *x=temp1,*y=temp2;
int sa[maxn];
int height[maxn];
int Comp(int *r,int a,int b,int l)
{
return r[a]==r[b] && r[a+l]==r[b+l];
}
void Da(int n,int m)
{
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) y[ p++ ]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
p=1;
swap(x,y);
x[ sa[0] ]=0;
for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
void Calc_height(int n)
{
height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!x[i]) continue;
int j=sa[ x[i]-1 ];
while (a[i+k]==a[j+k]) k++;
height[ x[i] ]=k;
}
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
int n;
scanf("%d",&n);
scanf("%s",&s);
scanf("%s",&s[n]);
for (int i=0; i<n; i++)
{
a[i]=(int)s[i]+1;
id[i]=1;
}
for (int i=0; i<n; i++)
{
a[n+1+i]=(int)s[n+i]+1;
id[n+1+i]=2;
}
a[n]=499;
id[n]=3;
n=n*2+2;
a[n-1]=0;
id[n-1]=4;
Da(n,500);
Calc_height(n);
int ans=0,ansid=0;
for (int i=1; i<n; i++)
if ( id[ sa[i-1] ]+id[ sa[i] ]==3 && height[i]>=ans )
{
ans=height[i];
ansid=sa[i];
}
for (int i=0; i<ans; i++) printf("%c",a[ansid+i]-1);
printf("\n");
return 0;
}
Timus Online Judge 1297:
题目大意:求原串的最长回文子串,相同长度则输出最前面一个。
分析:本人是用两次二分答案(由于单调性有点特殊),一次二分奇数长度,一次二分偶数。
其实可以正反串接起来做一遍后缀数组,然后枚举中间点。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=2010;
char s[maxn];
int a[maxn];
int id[maxn];
int cnt[maxn];
int temp1[maxn],temp2[maxn];
int *x=temp1,*y=temp2;
int sa[maxn];
int height[maxn];
bool h[maxn];
bool t[maxn];
int n,m;
int Comp(int *r,int p,int q,int l)
{
return r[p]==r[q] && r[p+l]==r[q+l];
}
void Da()
{
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) y[ p++ ]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) y[ p++ ]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ x[ y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ x[ y[i] ] ] ]=y[i];
p=1;
swap(x,y);
x[ sa[0] ]=0;
for (int i=1; i<n; i++) x[ sa[i] ]=Comp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
void Calc_height()
{
height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!x[i]) continue;
int j=sa[ x[i]-1 ];
while (a[i+k]==a[j+k]) k++;
height[ x[i] ]=k;
}
}
bool Judge(int len)
{
for (int i=0; i<n; i++) h[i]=t[i]=false;
int last=0;
for (int i=1; i<n; i++)
{
if (height[i]<len)
{
for (int j=last+1; j<i; j++)
{
int nid=id[ sa[j-1] ];
if (nid==1) h[ sa[j-1] ]=false;
if (nid==2) t[ n-2-sa[j-1] ]=false;
nid=id[ sa[j] ];
if (nid==1) h[ sa[j] ]=false;
if (nid==2) t[ n-2-sa[j] ]=false;
}
last=i;
}
else
{
int nid=id[ sa[i-1] ];
if (nid==1)
{
h[ sa[i-1] ]=true;
if (t[ sa[i-1]+len-1 ]) return true;
}
if (nid==2)
{
t[ n-2-sa[i-1] ]=true;
if ( n-sa[i-1]-len-1>=0 && h[ n-sa[i-1]-len-1 ] ) return true;
}
nid=id[ sa[i] ];
if (nid==1)
{
h[ sa[i] ]=true;
if (t[ sa[i]+len-1 ]) return true;
}
if (nid==2)
{
t[ n-2-sa[i] ]=true;
if ( n-sa[i]-len-1>=0 && h[ n-sa[i]-len-1 ] ) return true;
}
}
}
return false;
}
int Work(int len)
{
for (int i=0; i<n; i++) h[i]=t[i]=false;
int last=0,head=n;
for (int i=1; i<n; i++)
{
if (height[i]<len)
{
for (int j=last+1; j<i; j++)
{
int nid=id[ sa[j-1] ];
if (nid==1) h[ sa[j-1] ]=false;
if (nid==2) t[ n-2-sa[j-1] ]=false;
nid=id[ sa[j] ];
if (nid==1) h[ sa[j] ]=false;
if (nid==2) t[ n-2-sa[j] ]=false;
}
last=i;
}
else
{
int nid=id[ sa[i-1] ];
if (nid==1)
{
h[ sa[i-1] ]=true;
if (t[ sa[i-1]+len-1 ]) head=min(head,sa[i-1]);
}
if (nid==2)
{
t[ n-2-sa[i-1] ]=true;
if ( n-sa[i-1]-len-1>=0 && h[ n-sa[i-1]-len-1 ] )
head=min(head,n-sa[i-1]-len-1);
}
nid=id[ sa[i] ];
if (nid==1)
{
h[ sa[i] ]=true;
if (t[ sa[i]+len-1 ]) head=min(head,sa[i]);
}
if (nid==2)
{
t[ n-2-sa[i] ]=true;
if ( n-sa[i]-len-1>=0 && h[ n-sa[i]-len-1 ] )
head=min(head,n-sa[i]-len-1);
}
}
}
return head;
}
void Binary()
{
int ans=1;
int L=0,R=600;
while (L+1<R)
{
int mid=(L+R)>>1;
if ( Judge(mid*2) ) L=mid;
else R=mid;
}
ans=max(ans,L*2);
L=0,R=600;
while (L+1<R)
{
int mid=(L+R)>>1;
if ( Judge(mid*2+1) ) L=mid;
else R=mid;
}
ans=max(ans,L*2+1);
int head=Work(ans);
for (int i=0; i<ans; i++) printf("%c",s[head+i]);
printf("\n");
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%s",&s);
n=strlen(s);
for (int i=0; i<n; i++)
{
a[i]=(int)s[i]+1;
id[i]=1;
}
for (int i=n+1; i<=2*n; i++)
{
a[i]=a[2*n-i];
id[i]=2;
}
a[n]=499;
id[n]=3;
n=n*2+2;
a[n-1]=0;
id[n-1]=4;
m=500;
Da();
Calc_height();
Binary();
return 0;
}