给定一个长度为 n n n的仅由小写字母构成的字符串 S S S。我们定义一个字符串是好的,当且仅当它可以用两个不同的字母 x x x和 y y y表示成 x y x y x y x … xyxyxyx\dots xyxyxyx…的形式。比如字符串 a b a b abab abab、 t o t tot tot、 a a a是好的,但字符串 a b c abc abc、 a a aa aa不是好的。
现在有 q q q组询问,每次给定 1 ≤ l ≤ r ≤ n 1\leq l\leq r\leq n 1≤l≤r≤n,求 S S S的子串 S [ l ⋯ r ] S[l\cdots r] S[l⋯r]的最长的一个好的子序列的长度是多少,以及它可以被哪两个字符 x x x和 y y y来表示。如果有多个最长的串,则输出字典序最小的一个串的 x x x和 y y y。
1 ≤ n ≤ 1.5 × 1 0 6 , 1 ≤ m ≤ 1 0 5 1\leq n\leq 1.5\times 10^6,1\leq m\leq 10^5 1≤n≤1.5×106,1≤m≤105
对每个字符 i i i,考虑在只保留它时序列被分为若干段(???i??i???i??),预处理 z t i , j , k zt_{i,j,k} zti,j,k表示只保留字符 i i i时字符 j j j能否在第 k k k段出现,并用 f i , j , k f_{i,j,k} fi,j,k记录 z t i , j , k zt_{i,j,k} zti,j,k的前缀和。预处理 l t i , j lt_{i,j} lti,j和 r t i , j rt_{i,j} rti,j,分别表示在 i i i之前的第一个字符 j j j和在 i i i之后的第一个字符 j j j。因为所有 i i i的数量的和为 n n n,所以这部分的时间复杂度为 O ( n v ) O(nv) O(nv),其中 v v v为字符的个数,也就是小写字母的个数,即 26 26 26。
对于一组询问 l , r l,r l,r,枚举一种字符 i i i,用 l t lt lt和 r t rt rt来确定这段区间中 c c c第一次出现的位置 v l vl vl和最后一次出现的位置 v r vr vr,求出这两个位置的段标号 b l bl bl和 b r br br,再枚举第二种字符 j j j,那么整段的贡献为 f i , j , b r − f i , j , b l − 1 f_{i,j,br}-f_{i,j,bl-1} fi,j,br−fi,j,bl−1。对于两边的散块,用 l t lt lt和 r t rt rt来求贡献即可。这部分的时间复杂度为 O ( m v 2 ) O(mv^2) O(mv2)。
总时间复杂度为 O ( n v + m v 2 ) O(nv+mv^2) O(nv+mv2)。
#include
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;
}