任务量统计
工作日五道题 ( 5 / 5 ) (5/5) (5/5)
链接
https://cn.vjudge.net/problem/UVA-10829
题解
这题有点神啊
对于形如
A
B
A
ABA
ABA的串,我枚举
A
A
A的长度
u
u
u,然后把整个字符串按照
u
u
u分块,然后谜之乱搞…
我觉得按照
u
u
u分块的意义在于:我想要统计长度恰好为
u
u
u的满足条件的子串
A
A
A的个数,分块之后就带来一个便利,子串
A
A
A的左端点和右端点必然在相邻的两个块中(假设在同一个块中,那么区间长度小于
u
u
u;假设在不同的块中,那么区间长度大于
u
u
u)
那么分块之后我就有一个很显然的思路,在每个块中枚举左端点
l
l
l,再根据我枚举的
u
u
u确定串
A
A
A的位置,然后每个字符下标加上
u
+
g
u+g
u+g就得到了后面待匹配串的位置,进行暴力匹配从而统计答案
考虑怎么优化:既然每次比较的两个字符的坐标之差为
u
+
g
u+g
u+g,那么我是否可以先预处理一下对应位置的字符是不是相等?
如果相等我就放一个
1
1
1,不相等我就放一个
0
0
0,那么其实是统计左端点在我所枚举的块内,长度为
u
u
u的连续
1
1
1区间的个数。
假如我把每个长度为
u
u
u的块的范围设为左闭右开,可以发现所有的区间都包含了块的最后一个点(编号为
i
∗
u
−
1
i*u-1
i∗u−1),既然要统计包含这个点的全
1
1
1区间,我可以从这个点往前统计连续
1
1
1的个数
l
1
l1
l1,往后统计连续
1
1
1的个数
l
2
l2
l2,那么这个块对答案的贡献就是
m
a
x
(
0
,
l
1
+
l
2
−
l
+
1
)
max(0,l1+l2-l+1)
max(0,l1+l2−l+1)
后缀数组加速一下,每次查询
O
(
1
)
O(1)
O(1),总复杂度是
O
(
n
ln
(
n
)
)
O(n\ln(n))
O(nln(n))
总结
遇到一个字符串题,不一定先往算法上套。也许可以先想暴力怎么做,然后再用数据结构或者算法来优化。当然也不是绝对的,到底是先想数据结构/算法,还是先想怎么暴力,这二者还是要靠直觉和经验进行搭配。
一种口胡做法
这题可以等价于求
∣
s
a
x
−
s
a
y
∣
≤
l
c
p
(
x
,
y
)
+
g
|sa_x-sa_y|\leq lcp(x,y)+g
∣sax−say∣≤lcp(x,y)+g的点对
(
x
,
y
)
(x,y)
(x,y)的个数
l
c
p
lcp
lcp实际上就是一堆
h
e
i
g
h
t
height
height取
m
i
n
min
min,我先预处理出来每个
h
e
i
g
h
t
height
height作为
m
i
n
min
min的有效区间,然后这个
h
e
i
g
h
t
height
height就可以把有效区间分成两半,假设这个
h
e
i
g
h
t
height
height是
t
t
t,那么就是找
∣
s
a
x
−
s
a
y
∣
≤
t
+
g
|sa_x-sa_y| \leq t+g
∣sax−say∣≤t+g的
(
x
,
y
)
(x,y)
(x,y)的个数。这两段区间里肯定有一个较小的段,我先在这个区间枚举
i
i
i,然后用主席树到对应的另一半区间里去找符合条件的解,复杂度
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n)
(可能会写,也可能就这样扔着qwq)
代码
#include <bits/stdc++.h>
#define maxn 50010
#define maxk 17
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
struct SuffixArray
{
int sa[maxn], rank[maxn], ws[maxn], wv[maxn], wa[maxn], wb[maxn], height[maxn], st[maxk+2][maxn], N;
bool cmp(int *r, int a, int b, int l){return r[a]==r[b] and r[a+l]==r[b+l];}
void clear()
{
cl(sa), cl(rank), cl(ws), cl(wv), cl(wa), cl(wb), cl(height), cl(st);
}
void build(int *r, int n, int m)
{
N=n;
n++;
int i, j, k=0, p, *x=wa, *y=wb, *t;
for(i=0;i<m;i++)ws[i]=0;
for(i=0;i<n;i++)ws[x[i]=r[i]]++;
for(i=1;i<m;i++)ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--)sa[--ws[x[i]]]=i;
for(p=j=1;p<n;j<<=1,m=p)
{
for(p=0,i=n-j;i<n;i++)y[p++]=i;
for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
for(i=0;i<n;i++)wv[i]=x[y[i]];
for(i=0;i<m;i++)ws[i]=0;
for(i=0;i<n;i++)ws[wv[i]]++;
for(i=1;i<m;i++)ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--)sa[--ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,i=1,x[sa[0]]=0;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
for(i=0;i<n;i++)rank[sa[i]]=i;
for(i=0;i<n-1;height[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
void build_st() //st表
{
int i, k;
for(i=1;i<=N;i++)st[0][i]=height[i];
for(k=1;k<=maxk;k++)
for(i=1;i+(1<<k)-1<=N;i++)
st[k][i]=min(st[k-1][i],st[k-1][i+(1<<k-1)]);
}
int lcp(int x, int y) //最长公共前缀
{
int l=rank[x], r=rank[y];
if(l>r)swap(l,r);
if(l==r)return N-sa[l];
int t=log2(r-l);
return min(st[t][l+1],st[t][r-(1<<t)+1]);
}
}SA1, SA2;
int read(int x=0)
{
int c, f(1);
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
for(;isdigit(c);c=getchar())x=x*10+c-48;
return f*x;
}
char s[maxn];
int r[maxn];
int main()
{
int T=read(), len, g, i, L, R, mid, kase(0), u;
ll ans;
while(T--)
{
g=read();
scanf("%s",s);
len=strlen(s);
for(i=0;i<len;i++)r[i]=s[i]-'a'+1;
r[len]=0;
SA1.clear();
SA1.build(r,len,30);
SA1.build_st();
reverse(r,r+len);
r[len]=0;
SA2.clear();
SA2.build(r,len,30);
SA2.build_st();
ans=0ll;
for(u=1;u<=len;u++)
{
for(i=u-1;i<len;i+=u)
{
int l1, l2;
if(i+1+u+g>=len)l2=0;
else l2=min(u-1,SA1.lcp(i+1,i+1+g+u));
if(i+u+g>=len)break;
else l1=min(u,SA2.lcp(len-(i+u+g)-1,len-i-1));
ans+=max(0,l1+l2-u+1);
}
}
printf("Case %d: %lld\n",++kase,ans);
}
return 0;
}