题意:求n个串的最长公共子串,子串出现在一个串中可以是它的反转串出现。总长<=10^4.

题解:

【poj1226-出现或反转后出现在每个串的最长公共子串】后缀数组-LMLPHP

对于每个串,把反转串也连进去。
二分长度,分组,判断每个组。

 #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std; const int N=*;
int n,sl,cl,c[N],rk[N],sa[N],Rs[N],wr[N],y[N],h[N],st[N],ed[N],fst[N],fed[N];
char s[N];
bool vis[N]; void get_sa(int m)
{
for(int i=;i<=cl;i++) rk[i]=c[i];
for(int i=;i<=m;i++) Rs[i]=;
for(int i=;i<=cl;i++) Rs[rk[i]]++;
for(int i=;i<=m;i++) Rs[i]+=Rs[i-];
for(int i=cl;i>=;i--) sa[Rs[rk[i]]--]=i; int ln=,p=;
while(p<cl)
{
int k=;
for(int i=cl-ln+;i<=cl;i++) y[++k]=i;
for(int i=;i<=cl;i++) if(sa[i]>ln) y[++k]=sa[i]-ln; for(int i=;i<=cl;i++) wr[i]=rk[y[i]];
for(int i=;i<=m;i++) Rs[i]=;
for(int i=;i<=cl;i++) Rs[wr[i]]++;
for(int i=;i<=m;i++) Rs[i]+=Rs[i-];
for(int i=cl;i>=;i--) sa[Rs[wr[i]]--]=y[i]; for(int i=;i<=cl;i++) wr[i]=rk[i];
for(int i=cl+;i<=cl+ln;i++) wr[i]=;
p=;rk[sa[]]=;
for(int i=;i<=cl;i++)
{
if(wr[sa[i]]!=wr[sa[i-]] || wr[sa[i]+ln]!=wr[sa[i-]+ln]) p++;
rk[sa[i]]=p;
}
ln*=,m=p;
}
sa[]=,rk[]=;
} void get_h()
{
int k=,j;
for(int i=;i<=cl;i++) if(rk[i]!=)
{
j=sa[rk[i]-];
if(k) k--;
while(c[i+k]==c[j+k] && i+k<=cl && j+k<=cl) k++;
h[rk[i]]=k;
}
h[]=;
} int idx(int x)
{
for(int i=;i<=n;i++)
if((st[i]<=x && x<=ed[i]) || (fst[i]<=x && x<=fed[i])) return i;
} bool check(int k)
{
memset(vis,,sizeof(vis));
for(int i=;i<=cl;i++)
{
if(h[i]<k)
{
int cnt=;
for(int j=;j<=n;j++) if(vis[j]) cnt++;
if(cnt==n) return ;
memset(vis,,sizeof(vis));
}
vis[idx(sa[i])]++;
}
int cnt=;
for(int j=;j<=n;j++) if(vis[j]) cnt++;
if(cnt==n) return ;
return ;
} int main()
{
freopen("a.in","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
int num=;
cl=;
for(int i=;i<=n;i++)
{
scanf("%s",s+);
sl=strlen(s+);
if(i>) c[++cl]=++num;
st[i]=cl+;for(int j=;j<=sl;j++) c[++cl]=s[j];ed[i]=cl;
c[++cl]=++num;
fst[i]=cl+;for(int j=sl;j>=;j--) c[++cl]=s[j];fed[i]=cl;
}
if(n==) {printf("%d\n",sl);continue;}
get_sa();
get_h();
// for(int i=1;i<=cl;i++) printf("%c",c[i]);printf("\n");
// for(int i=1;i<=cl;i++) printf("%d ",c[i]);printf("\n");
// for(int i=1;i<=cl;i++) printf("%d ",sa[i]);printf("\n");
// for(int i=1;i<=cl;i++) printf("%d ",rk[i]);printf("\n");
// for(int i=1;i<=cl;i++) printf("%d ",h[i]);printf("\n");
int l=,r=cl,mid;
while(l<r)
{
mid=(l+r+)/;
if(check(mid)) l=mid;
else r=mid-;
}
printf("%d\n",l);
}
return ;
}

这一题我曾经用kmp暴力水过。。贴一下代码

 #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std; const int N=,Inf=(int)1e9;
char s[N][N],c1[N],c2[N];
int n,id,cl,l[N],nt[N],nxt[N]; void kmp_nt()
{
nt[]=;
for(int i=,p=;i<=cl;i++)
{
while(c1[p+]!=c1[i] && p) p=nt[p];
if(c1[p+]==c1[i]) p++;
nt[i]=p;
} nxt[]=;
for(int i=,p=;i<=cl;i++)
{
while(c2[p+]!=c2[i] && p) p=nxt[p];
if(c2[p+]==c2[i]) p++;
nxt[i]=p;
}
} bool kmp_td(int x)
{
for(int i=,p=;i<=l[x];i++)
{
while(c1[p+]!=s[x][i] && p) p=nt[p];
if(c1[p+]==s[x][i]) p++;
if(p==cl) return ;
}
for(int i=,p=;i<=l[x];i++)
{
while(c2[p+]!=s[x][i] && p) p=nxt[p];
if(c2[p+]==s[x][i]) p++;
// td[i]=p;
if(p==cl) return ;
}
return ;
} bool check(int len)
{
for(int i=;i+len-<=l[id];i++)
{
cl=;
for(int j=i;j<=i+len-;j++)
c1[++cl]=s[id][j],c2[len-cl+]=s[id][j];
kmp_nt();
bool bk=;
for(int j=;j<=n;j++)
if(!kmp_td(j)) {bk=;break;}
if(bk) return ;
}
} int main()
{
freopen("a.in","r",stdin);
freopen("vio.out","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
int ll=,rr=Inf,mid;
for(int i=;i<=n;i++)
{
scanf("%s",s[i]+);
l[i]=strlen(s[i]+);
if(l[i]<rr) rr=l[i],id=i;
}
while(ll<rr)
{
mid=(ll+rr+)/;
if(check(mid)) ll=mid;
else rr=mid-;
}
printf("%d\n",ll);
}
return ;
}
05-11 20:26