2023NOIP A层联测10-子序列

给定一个长为 n n n 的仅有小写英文字母构成字符串 S = S 1 S 2 ⋯ S n S=S_1S_2\cdots S_n S=S1S2Sn。我们定义一个字符串是好的,当且仅当它可以用两个不同的字母 xy 表示成 xyxyxyx... 的形式。例如,字符串 ababtotz 是好的,但字符串 abcaa 不是好的。

现在有 q q q 组询问,每次给定 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1lrn,你想要求出,对于串 S S S 的子串 S [ l ⋯ r ] S[l \cdots r] S[lr],它最长的一个好的子序列的长度是多少,以及它可以被哪两个不同字符 xy 来表示。如果有多个最长的串,则输出字典序最小的一个串的 xy


预处理出 p r e i , j , n x t i , j pre_{i,j},nxt_{i,j} prei,j,nxti,j 分别表示第 i i i 个字符前/后第一个字符 j j j 的出现位置。

对于每个字符 S i S_i Si,考虑在只保留它时序列被分成了若干段(?????i?i??i????i?? ),预处理出 s u m i , j sum_{i,j} sumi,j 表示以 j j j 字符为分隔点,将序列分成若干段, S i S_i Si 所在的段及其之后的段含有 S i S_i Si 的个数减一。不懂?放个图就懂了。

2023NOIP A层联测10-子序列_第1张图片 可以递推求出 s u m i , j sum_{i,j} sumi,j

s u m i , j = s u m n x t i , S i + [ n x t i , S i > n x t i , j ] sum_{i,j}=sum_{nxt_{i,S_i}}+[nxt_{i,S_i}>nxt_{i,j}] sumi,j=sumnxti,Si+[nxti,Si>nxti,j]

意思就是如果 j j j 夹在 S i S_i Si 中间,就可以从后面转移过来。

对于每一次询问,枚举答案的两个字符 c 1 , c 2 c_1,c_2 c1,c2,找出区间内第一次和最后一次 c 1 c_1 c1 出现的位置 L , R L,R L,R 2 ( s u m L , c 1 − s u m R , c 1 ) + 1 + [ p r e r + 1 , c 2 > R ] 2(sum_{L,c_1}-sum_{R,c_1})+1+[pre_{r+1,c_2}>R] 2(sumL,c1sumR,c1)+1+[prer+1,c2>R] 就是最长的好子序列长度。这样就求出了答案。

具体实现看代码

#include
using namespace std;
const int N=1.5e6+2;
char a[N];
int m,pre[N][26],nxt[N][26],sum[N][26];
int main()
{
    freopen("seq.in","r",stdin);
    freopen("seq.out","w",stdout);
    scanf("%s",a+1);
    int n=strlen(a+1);
    for(int i=1;i<=n+1;i++){
        for(int j=0;j<26;j++){
            if(a[i-1]==j+97) pre[i][j]=i-1;
            else pre[i][j]=pre[i-1][j];
        }
    }
    for(int i=0;i<26;i++) nxt[n+1][i]=n+1;
    for(int i=n;i>=0;i--){
        for(int j=0;j<26;j++){
            if(a[i+1]==j+97) nxt[i][j]=i+1;
            else nxt[i][j]=nxt[i+1][j];
        }
        for(int j=0;j<26;j++){
            sum[i][j]=sum[nxt[i][a[i]-97]][j]+(nxt[i][a[i]-97]>nxt[i][j]);
        }
    }
    scanf("%d",&m);
    for(int i=1,l,r;i<=m;i++){
        scanf("%d%d",&l,&r);
        int maxn=-1;
        char c1=0,c2=0;
        for(int i=0;i<26;i++){
            for(int j=0;j<26;j++){
                if(i==j) continue;
                if(nxt[l-1][i]>r) continue;
                int L=nxt[l-1][i],R=pre[r+1][i];
                int ans=(sum[L][j]-sum[R][j])*2+1+(pre[r+1][j]>R);
                if(ans>maxn) maxn=ans,c1=i+97,c2=j+97;
            }
        }
        printf("%d %c%c\n",maxn,c1,c2);
    }
}

你可能感兴趣的:(算法,c++)