这题我觉得就是实现和卡常数!!!
不会后缀数组的或者背模板的请先去好好研究后缀数组,至少要有自己的版本,当然,自己的版本至少要经过各大OJ洗礼后才能出炉。
由于不会SpareTable算法,直接zkw线段树。
还是一样,将所有串连起来后缀排序,对于每个字,枚举匹配的位置,O(logn)zkw或O(1)ST的查询匹配长度,按照查询结果跳指针,最多K次不匹配,最多跳K次,然后就统计即可。这一步O(nmklogn)zkw或O(nmk)ST
第二问,郭华阳学长的题解有点错误,但思想很清晰
记g[i][j]为从第i位开始以第j个字为第一个匹配的字的最大匹配数
d[i][j]为在g[i][j]情况下的方案数
f[i]为第i位开始的最大匹配数
c[i]为f[i]情况下的方案数
预处理f[n],c[n],g[n][j],d[n][j]
按照贪心思想,题目只要求本质不同的方案数,所以转移就很简单了
做第一问时为第二问预处理leng[i][j]表示在以i处开始匹配j时的最短长度,ok[i][j]表示以i处开始是否可匹配j
从后往前枚举i先转移g,d,再转移f,c
g[i][j]={
max(g[i+1][j],f[t]+1);ok[i][j]为true
否则g[i+1][j];
}
d[i][j]={
max(c[t],1);ok[i][j]为true
否则d[i+1][j];
}
其中t=i+leng[i][j]
f[i]=max(g[i][j]);
c[i]=max(sigma(d[i][j])(g[i][j]==f[i]),1)
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define mo 1000003
int a[2100025],count[2100025],ran[2100025],te[2100025],sa[2100025],news[2100025];
int hei[4194309];
int len[25],left[25],right[25];
int n=0,m=0,maink=0,mm=0,maxv=0,ans1=0,ans2=0,totn=0;
int f[100002],c[100002];
int g[100002][21],delta[100002][21],leng[100002][21],d[100002][21];
bool ok[100002][21];
inline int min(int a,int b)
{
return a<b?a:b;
}
inline int max(int a,int b)
{
return a>b?a:b;
}
bool cmp(int *s,int a,int b,int l)
{
return (s[a]==s[b] && s[a+l]==s[b+l]);
}
void doualgo(int *s,int n,int m)
{
int i=0,l=0,p=1,*rank=ran,*tem=te,*t=NULL;
memset(count,0,sizeof(count));
for (i=1;i<=n;i++) count[rank[i]=s[i]]++;
for (i=1;i<=m;i++) count[i]+=count[i-1];
for (i=n;i>=1;i--) sa[count[rank[i]]--]=i;
for (l=1;p<n;m=p,l*=2)
{
for (p=0,i=n-l+1;i<=n;i++) tem[++p]=i;
for (i=1;i<=n;i++) if (sa[i]>l) tem[++p]=sa[i]-l;
memset(count,0,sizeof(count));
for (i=1;i<=n;i++) count[news[i]=rank[tem[i]]]++;
for (i=1;i<=m;i++) count[i]+=count[i-1];
for (i=n;i>=1;i--) sa[count[news[i]]--]=tem[i];
for (t=rank,rank=tem,tem=t,t=NULL,rank[sa[0]]=0,p=0,i=1;i<=n;i++)
rank[sa[i]]=cmp(tem,sa[i],sa[i-1],l)?p:++p;
}
}
void calhei(int *s,int n)
{
int i=0,j=0,k=0;
memset(ran,0,sizeof(ran));
for (i=1;i<=n;i++) ran[sa[i]]=i;
for (i=1;i<=n;hei[ran[i++]+mm]=k)
for (k?k--:k=0,j=sa[ran[i]-1];s[i+k]==s[j+k];k++);
}
void buildzkw()
{
int i=0;
for (i=mm-1;i>=1;i--)
hei[i]=min(hei[i<<1],hei[(i<<1)+1]);
}
int ask(int l,int r)
{
l=ran[l];
r=ran[r];
if (l>r)
{
int tmp=l;
l=r;
r=tmp;
}
l=l+mm;
r=r+mm+1;
int o=n+1;
while (l^r^1)
{
if (!(l&1)) o=o>hei[l^1]?hei[l^1]:o;
if (r&1) o=o>hei[r^1]?hei[r^1]:o;
l>>=1;
r>>=1;
}
return o;
}
int main()
{
freopen("voice.in","r",stdin);
freopen("voice.out","w",stdout);
scanf("%d%d%d",&n,&m,&maink);
int i=0,j=0;
for (i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]++;
if (a[i]>maxv) maxv=a[i];
}
totn=n;
for (i=1;i<=m;i++)
{
scanf("%d",&len[i]);
left[i]=totn+1;
for (j=1;j<=len[i];j++)
{
scanf("%d",&a[++totn]);
a[totn]++;
if (a[totn]>maxv) maxv=a[totn];
}
right[i]=totn;
}
mm=1<<(int)(log2(totn)+1);
if (mm<=totn) mm<<=1;
doualgo(a,totn,maxv);
calhei(a,totn);
buildzkw();
int now=0,pmain=0,ppatt=0,distinc=0,last=0,common=0;
bool flag=0;
for (now=1;now<=m;now++)
{
flag=0;
for (i=1;i<=n-len[now]+1;i++)
{
pmain=i,ppatt=left[now];
distinc=0;
while (distinc<=maink && pmain<=n && ppatt<=right[now])
{
common=min(min(ask(pmain,ppatt),n-pmain+1),right[now]-ppatt+1);
ppatt+=common;
pmain+=common+1;
distinc++;
}
if (ppatt>right[now])
{
flag=1;
delta[i][now]=(min(maink-distinc+1,n-pmain+2)+1);
ans2+=delta[i][now];
leng[i][now]=pmain-i-1;
ok[i][now]=1;
}
}
if (flag) ans1++;
}
printf("%d %d\n",ans1,ans2);
int l=0,t=0;
for (i=1;i<=m;i++)
{
if (ok[n][i])
{
g[n][i]=1;
d[n][i]=1;
f[n]=1;
c[n]++;
}
}
c[n]=max(c[n],1);
for (i=n-1;i>=1;i--)
{
for (j=1;j<=m;j++)
{
if (ok[i][j])
{
t=i+leng[i][j];
g[i][j]=max(g[i+1][j],f[t]+1);
d[i][j]=max(c[t],1);
}
else
{
d[i][j]=d[i+1][j];
g[i][j]=g[i+1][j];
}
f[i]=max(f[i],g[i][j]);
}
for (j=1;j<=m;j++)
if (f[i]==g[i][j])
{
c[i]+=d[i][j];
if (c[i]>mo) c[i]-=mo;
}
c[i]=max(c[i],1);
}
printf("%d %d\n",f[1],c[1]);
return 0;
}