题解:将p串翻转,间隔不同字符连接起来,进行一次后缀数组的操作。若strange set(Q)>0,那么Q肯定是p串中一个的子串。所以Q串翻转后,二分直接在后缀数组中找到边界位置,lower和upper。
当不存在时,答案为0;
当lower==upper时,答案为最大长度(sa[lower]到间隔字符的长度)-最小长度(max(height[lower],height[upper+1])+1)+1
当lower<upper时,
与Q串数量相同的最大长度是min(height[lower+1],……,height[upper]),若长度再加1,那么子集数将会减少;
与Q串数量相同的最小长度是max(height[lower],height[upper+1])+1
两者相减就是答案。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=2e5+10;
char str[N];
int s[N],pos[N];
int sa[N],t[N],t2[N],c[N],n;
void build_sa(int m){
int i,*x=t,*y=t2;
for (i=0;i<m;i++)c[i]=0;
for (i=0;i<n;i++)c[x[i]=s[i]]++;
for (i=1;i<m;i++)c[i]+=c[i-1];
for (i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
for (int k=1;k<=n;k<<=1){
int p=0;
for (i=n-k;i<n;i++)y[p++]=i;
for (i=0;i<n;i++)if (sa[i]>=k)y[p++]=sa[i]-k;
for (i=0;i<m;i++)c[i]=0;
for (i=0;i<n;i++)c[x[y[i]]]++;
for (i=0;i<m;i++)c[i]+=c[i-1];
for (i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;x[sa[0]]=0;
for (i=1;i<n;i++)
x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
if (p>=n)break;
m=p;
}
}
int Rank[N],height[N];
int dp[N][20];
void getHeight(){
int i,j,k=0;
for (i=0;i<n;i++)Rank[sa[i]]=i;
for (i=0;i<n;i++){
if (k)k--;
int j=sa[Rank[i]-1];
while (s[i+k]==s[j+k])k++;
dp[Rank[i]][0]=height[Rank[i]]=k;
}
for (int k=1;(1<<k)<=n;k++)for (int i=0;i+(1<<k)-1<n;i++)
dp[i][k]=min(dp[i][k-1],dp[i+(1<<(k-1))][k-1]);
}
int cmp(int x,int Len)
{
for (int i=0;i<Len;i++)
if (s[x+i]<t[i])return -1;else if (s[x+i]>t[i])return 1;
return 0;
}
int getmin(int L,int R)
{
int k=0;while ((1<<(k+1))<=R-L+1)k++;
return min(dp[L][k],dp[R-(1<<k)+1][k]);
}
void work()
{
int m;
scanf("%d%d",&n,&m);
int nn=0;
for (int bp=1;bp<=n;bp++){
scanf("%s",str);int Len=strlen(str);
reverse(str,str+Len);
for (int i=0;i<Len;i++)s[i+nn]=str[i]-'a'+1,pos[i+nn]=Len+nn;
s[Len+nn]=26+bp;pos[Len+nn]=Len+nn;nn+=Len+1;
}
s[nn]=0;nn++;
swap(nn,n);build_sa(nn+27);//需要有一个比'a'小的字符,然后m不要开小
getHeight();
while (m--){
scanf("%s",str);int Len=strlen(str);
reverse(str,str+Len);
for (int i=0;i<Len;i++)t[i]=str[i]-'a'+1;t[Len]=0;
int lower=-1,upper;
int L=0,R=n-1;
while (L<=R){
int mid=(L+R)>>1;
int tmp=cmp(sa[mid],Len);
if (tmp==0){
lower=mid;R=mid-1;
}else if (tmp>0)R=mid-1;
else L=mid+1;
}
if (lower==-1){puts("0");continue;}
L=0,R=n-1;
while (L<=R){
int mid=(L+R)>>1;
int tmp=cmp(sa[mid],Len);
if (tmp==0){
upper=mid;L=mid+1;
}else if (tmp>0)R=mid-1;
else L=mid+1;
}
if (lower==upper)printf("%d\n",pos[sa[lower]]-sa[lower]-max(height[lower],height[upper+1]));else{
printf("%d\n",getmin(lower+1,upper)-max(height[lower],height[upper+1]));
}
}
}
int main()
{
freopen("data.txt","r",stdin);
freopen("test.txt","w",stdout);
int Case;scanf("%d",&Case);
for (int C=1;C<=Case;C++){
printf("Case #%d:\n",C);
work();
}
return 0;
}