题目大意
给定一个长度为nnn的仅由小写字母构成的字符串SSS。我们定义一个字符串是好的,当且仅当它可以用两个不同的字母xxx和yyy表示成xyxyxyx…xyxyxyx\dotsxyxyxyx…的形式。比如字符串abababababab、tottottot、aaa是好的,但字符串abcabcabc、aaaaaa不是好的。
现在有qqq组询问,每次给定1≤l≤r≤n1\leq l\leq r\leq n1≤l≤r≤n,求SSS的子串S[l⋯r]S[l\cdots r]S[l⋯r]的最长的一个好的子序列的长度是多少,以及它可以被哪两个字符xxx和yyy来表示。如果有多个最长的串,则输出字典序最小的一个串的xxx和yyy。
1≤n≤1.5×106,1≤m≤1051\leq n\leq 1.5\times 10^6,1\leq m\leq 10^51≤n≤1.5×106,1≤m≤105
题解
对每个字符iii,考虑在只保留它时序列被分为若干段(???i??i???i??),预处理zti,j,kzt_{i,j,k}zti,j,k表示只保留字符iii时字符jjj能否在第kkk段出现,并用fi,j,kf_{i,j,k}fi,j,k记录zti,j,kzt_{i,j,k}zti,j,k的前缀和。预处理lti,jlt_{i,j}lti,j和rti,jrt_{i,j}rti,j,分别表示在iii之前的第一个字符jjj和在iii之后的第一个字符jjj。因为所有iii的数量的和为nnn,所以这部分的时间复杂度为O(nv)O(nv)O(nv),其中vvv为字符的个数,也就是小写字母的个数,即262626。
对于一组询问l,rl,rl,r,枚举一种字符iii,用ltltlt和rtrtrt来确定这段区间中ccc第一次出现的位置vlvlvl和最后一次出现的位置vrvrvr,求出这两个位置的段标号blblbl和brbrbr,再枚举第二种字符jjj,那么整段的贡献为fi,j,br−fi,j,bl−1f_{i,j,br}-f_{i,j,bl-1}fi,j,br−fi,j,bl−1。对于两边的散块,用ltltlt和rtrtrt来求贡献即可。这部分的时间复杂度为O(mv2)O(mv^2)O(mv2)。
总时间复杂度为O(nv+mv2)O(nv+mv^2)O(nv+mv2)。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1500000,M=100000,K=26;
int n,m,l,r,ans,zt[N+5],id[N+5],lt[K][N+5],rt[K][N+5];
char v1,v2,s[N+5];
vector<int>w[K],f[K][K];
int main()
{
// freopen("seq.in","r",stdin);
// freopen("seq.out","w",stdout);
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++){
id[i]=w[s[i]-'a'].size();
w[s[i]-'a'].push_back(i);
}
for(int i=0;i<26;i++){
for(int k=0;k+1<w[i].size();k++){
for(int j=w[i][k]+1;j<w[i][k+1];j++){
zt[s[j]-'a']=1;
}
for(int j=0;j<26;j++){
f[i][j].push_back(zt[j]);zt[j]=0;
if(k) f[i][j][k]=f[i][j][k]+f[i][j][k-1];
}
}
}
for(int i=0;i<26;i++){
lt[i][0]=0;
for(int j=1;j<=n;j++){
lt[i][j]=lt[i][j-1];
if(s[j]-'a'==i) lt[i][j]=j;
}
rt[i][n+1]=n+1;
for(int j=n;j>=1;j--){
rt[i][j]=rt[i][j+1];
if(s[j]-'a'==i) rt[i][j]=j;
}
}
scanf("%d",&m);
while(m--){
scanf("%d%d",&l,&r);
ans=0;
for(int i=0;i<26;i++){
int vl=rt[i][l],vr=lt[i][r];
if(vl>vr) continue;
if(vl>=n+1||vr<=0) continue;
int bl=id[vl],br=id[vr];
for(int j=0;j<26;j++){
if(i==j) continue;
if(rt[i][l]>rt[j][l]) continue;
int tmp=0;
if(br) tmp+=f[i][j][br-1];
if(bl) tmp-=f[i][j][bl-1];
tmp=tmp*2+1;
if(rt[j][vr]<=r||lt[j][vl]>=l) ++tmp;
if(tmp>ans){
ans=tmp;
v1='a'+i;v2='a'+j;
}
}
}
printf("%d %c%c\n",ans,v1,v2);
}
return 0;
}

文章讨论了如何解决一个关于字符串的好子序列查询问题,通过预处理字符出现频率和段落信息,利用动态规划和字符组合分析,以O(nv+mv^2)的时间复杂度求解最长好子序列及其表示的两个字符。
134

被折叠的 条评论
为什么被折叠?



